Added industries collection
This commit is contained in:
parent
1ad6bf17cb
commit
ccbfe642a8
19 changed files with 277 additions and 58 deletions
14
bin/eu-eea-e-prtr.awk
Executable file
14
bin/eu-eea-e-prtr.awk
Executable file
|
|
@ -0,0 +1,14 @@
|
||||||
|
# cat dbo.PUBLISH_FACILITYREPORT.csv| ./eu-eea-e-prtr.awk | mongoimport -d "fuegos" -c "industries"
|
||||||
|
|
||||||
|
gawk -F',' 'NR>1 { \
|
||||||
|
gsub(/"/,"",$6);
|
||||||
|
gsub(/"/,"",$5);
|
||||||
|
gsub(/\t/,"",$6);
|
||||||
|
gsub(/\t/,"",$5);
|
||||||
|
gsub(/\x1a/,"",$5);
|
||||||
|
gsub(/\x1a/,"",$6);
|
||||||
|
lon = $14+0;
|
||||||
|
lat = $13+0;
|
||||||
|
if (lon != 0 && lat != 0 && lon > -180 && lon < 180 && lat >= -90 && lat <= 90)
|
||||||
|
printf("{name:\"%s, %s\",geo:{type:\"Point\",coordinates:[%s,%s]},registry:\"1\"}\n" \
|
||||||
|
,$6,$5,lon,lat)}'
|
||||||
10
bin/us-epa-frs.awk
Executable file
10
bin/us-epa-frs.awk
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# cat NATIONAL_SINGLE.CSV| ./us-epa-frs.awk | mongoimport -d "fuegos" -c "industries"
|
||||||
|
|
||||||
|
gawk -F',' 'NR>1{ \
|
||||||
|
gsub(/"/,"",$33);
|
||||||
|
gsub(/"/,"",$32);
|
||||||
|
gsub(/"/,"",$3);
|
||||||
|
gsub(/\t/,"",$3);
|
||||||
|
lon = $33+0;
|
||||||
|
lat = $32+0;
|
||||||
|
if (lon != 0 && lat != 0 && lon > -180 && lon < 180 && lat >= -90 && lat <= 90) { printf("{name:\"%s\",geo:{type:\"Point\",coordinates:[%s,%s]},registry:\"2\"}\n",name,lon,lat)}}'
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { check } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import { NumberBetween } from '/imports/modules/server/other-checks';
|
import { NumberBetween } from '/imports/modules/server/other-checks';
|
||||||
import { whichAreFalsePositives } from '/imports/api/FalsePositives/server/publications';
|
import { whichAreFalsePositives, firesUnion } from '/imports/api/FalsePositives/server/publications';
|
||||||
|
import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
|
||||||
|
import Industries from '/imports/api/Industries/Industries';
|
||||||
import ActiveFires from '../ActiveFires';
|
import ActiveFires from '../ActiveFires';
|
||||||
|
|
||||||
const counter = new Counter('countActiveFires', ActiveFires.find({}));
|
const counter = new Counter('countActiveFires', ActiveFires.find({}));
|
||||||
|
|
@ -37,8 +39,10 @@ const activefires = (northEastLng, northEastLat, southWestLng, southWestLat, wit
|
||||||
// console.log(`Fires total: ${fires.count()}`);
|
// console.log(`Fires total: ${fires.count()}`);
|
||||||
|
|
||||||
if (withMarks && fires.fetch().length > 0) {
|
if (withMarks && fires.fetch().length > 0) {
|
||||||
const falsePos = whichAreFalsePositives(fires);
|
const union = firesUnion(fires);
|
||||||
return [fires, falsePos];
|
const falsePos = whichAreFalsePositives(FalsePositives, union);
|
||||||
|
const industries = whichAreFalsePositives(Industries, union);
|
||||||
|
return [fires, falsePos, industries];
|
||||||
}
|
}
|
||||||
|
|
||||||
return fires;
|
return fires;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import { check } from 'meteor/check';
|
||||||
import { NumberBetween } from '/imports/modules/server/other-checks';
|
import { NumberBetween } from '/imports/modules/server/other-checks';
|
||||||
import L from 'leaflet-headless';
|
import L from 'leaflet-headless';
|
||||||
import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
|
import calcUnion from '/imports/ui/components/Maps/SubsUnion/Unify';
|
||||||
|
import Industries from '/imports/api/Industries/Industries';
|
||||||
import FalsePositives from '../FalsePositives';
|
import FalsePositives from '../FalsePositives';
|
||||||
|
|
||||||
const counter = new Counter('countFalsePositives', FalsePositives.find({}));
|
const counter = new Counter('countFalsePositives', FalsePositives.find({}));
|
||||||
|
|
@ -15,18 +16,21 @@ Meteor.publish('falsePositivesTotal', function total() {
|
||||||
return counter;
|
return counter;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const whichAreFalsePositives = (fires) => {
|
export const firesUnion = (fires) => {
|
||||||
const group = new L.FeatureGroup();
|
const group = new L.FeatureGroup();
|
||||||
const remap = fires.fetch().map(function remap(doc) {
|
const remap = fires.fetch().map(function remap(doc) {
|
||||||
// default scan: 1 for neightbor alerts
|
// default scan: 1 for neightbor alerts
|
||||||
return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan || 1 };
|
return { location: { lat: doc.lat, lon: doc.lon }, distance: doc.scan || 1 };
|
||||||
});
|
});
|
||||||
const result = calcUnion(remap, group, sub => sub);
|
const union = calcUnion(remap, group, sub => sub);
|
||||||
|
return union;
|
||||||
|
};
|
||||||
|
|
||||||
const falsePos = FalsePositives.find({
|
export const whichAreFalsePositives = (collection, union) => {
|
||||||
|
const result = collection.find({
|
||||||
geo: {
|
geo: {
|
||||||
$geoWithin: {
|
$geoWithin: {
|
||||||
$geometry: result[0].geometry
|
$geometry: union[0].geometry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
@ -40,11 +44,11 @@ export const whichAreFalsePositives = (fires) => {
|
||||||
|
|
||||||
/* console.log(`False positive total: ${falsePos.count()}`);
|
/* console.log(`False positive total: ${falsePos.count()}`);
|
||||||
console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
|
console.log(`False positives: ${JSON.stringify(falsePos.fetch())}`); */
|
||||||
return falsePos;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const falsePositives = (northEastLng, northEastLat, southWestLng, southWestLat) => {
|
const find = (collection, northEastLng, northEastLat, southWestLng, southWestLat) => {
|
||||||
const fires = FalsePositives.find({
|
const fires = collection.find({
|
||||||
geo: {
|
geo: {
|
||||||
$geoWithin: {
|
$geoWithin: {
|
||||||
$box: [
|
$box: [
|
||||||
|
|
@ -60,19 +64,6 @@ const falsePositives = (northEastLng, northEastLat, southWestLng, southWestLat)
|
||||||
// when: 1,
|
// when: 1,
|
||||||
fireId: 1
|
fireId: 1
|
||||||
}
|
}
|
||||||
}).serverTransform(function transformDoc(odoc) {
|
|
||||||
const doc = odoc;
|
|
||||||
// Destructuring gives me an error: "Cannot destructure property `geo` of 'undefined'"
|
|
||||||
const geo = doc.geo;
|
|
||||||
if (geo) {
|
|
||||||
doc.lat = geo.coordinates[1];
|
|
||||||
doc.lon = geo.coordinates[0];
|
|
||||||
}
|
|
||||||
doc._id = doc.fireId;
|
|
||||||
doc.id = doc.fireId;
|
|
||||||
delete doc.geo;
|
|
||||||
// console.log(doc);
|
|
||||||
return doc;
|
|
||||||
});
|
});
|
||||||
// console.log(`Fires total: ${fires.count()}`);
|
// console.log(`Fires total: ${fires.count()}`);
|
||||||
return fires;
|
return fires;
|
||||||
|
|
@ -85,5 +76,15 @@ Meteor.publishTransformed('falsePositivesMyloc', function falsePositivesInMyLoc(
|
||||||
check(southWestLng, NumberBetween(-180, 180));
|
check(southWestLng, NumberBetween(-180, 180));
|
||||||
check(northEastLat, NumberBetween(-90, 90));
|
check(northEastLat, NumberBetween(-90, 90));
|
||||||
|
|
||||||
return falsePositives(northEastLng, northEastLat, southWestLng, southWestLat);
|
return find(FalsePositives, northEastLng, northEastLat, southWestLng, southWestLat);
|
||||||
|
});
|
||||||
|
|
||||||
|
Meteor.publishTransformed('industriesMyloc', function industriesInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat) {
|
||||||
|
// latitude -90 and 90 and the longitude between -180 and 180
|
||||||
|
check(northEastLng, NumberBetween(-180, 180));
|
||||||
|
check(southWestLat, NumberBetween(-90, 90));
|
||||||
|
check(southWestLng, NumberBetween(-180, 180));
|
||||||
|
check(northEastLat, NumberBetween(-90, 90));
|
||||||
|
|
||||||
|
return find(Industries, northEastLng, northEastLat, southWestLng, southWestLat);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ import NodeGeocoder from 'node-geocoder';
|
||||||
import { gmapServerKey } from '/imports/startup/server/IPGeocoder';
|
import { gmapServerKey } from '/imports/startup/server/IPGeocoder';
|
||||||
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
|
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
|
||||||
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
||||||
import { whichAreFalsePositives } from '/imports/api/FalsePositives/server/publications';
|
import { whichAreFalsePositives, firesUnion } from '/imports/api/FalsePositives/server/publications';
|
||||||
|
import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
|
||||||
|
import Industries from '/imports/api/Industries/Industries';
|
||||||
|
|
||||||
import FiresCollection from '../Fires';
|
import FiresCollection from '../Fires';
|
||||||
|
|
||||||
function findFire(unsealed) {
|
function findFire(unsealed) {
|
||||||
|
|
@ -115,8 +118,10 @@ Meteor.publish('fireFromId', function fireFromId(_id) {
|
||||||
const fire = FiresCollection.find(new Meteor.Collection.ObjectID(_id));
|
const fire = FiresCollection.find(new Meteor.Collection.ObjectID(_id));
|
||||||
if (fire.count() !== 0) {
|
if (fire.count() !== 0) {
|
||||||
// console.info(`Archive fire found: ${_id}`);
|
// console.info(`Archive fire found: ${_id}`);
|
||||||
const falsePos = whichAreFalsePositives(fire);
|
const union = firesUnion(fire);
|
||||||
return [fire, falsePos];
|
const falsePos = whichAreFalsePositives(FalsePositives, union);
|
||||||
|
const industries = whichAreFalsePositives(Industries, union);
|
||||||
|
return [fire, falsePos, industries];
|
||||||
}
|
}
|
||||||
console.info(`Fire not found: ${_id}`);
|
console.info(`Fire not found: ${_id}`);
|
||||||
// Not found in active fires!
|
// Not found in active fires!
|
||||||
|
|
|
||||||
39
imports/api/Industries/Industries.js
Normal file
39
imports/api/Industries/Industries.js
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* eslint-disable consistent-return */
|
||||||
|
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import LocationSchema from '/imports/api/Utility/LocationSchema.js';
|
||||||
|
|
||||||
|
const Industries = new Mongo.Collection('industries');
|
||||||
|
|
||||||
|
Industries.allow({
|
||||||
|
insert: () => false,
|
||||||
|
update: () => false,
|
||||||
|
remove: () => false
|
||||||
|
});
|
||||||
|
|
||||||
|
Industries.deny({
|
||||||
|
insert: () => true,
|
||||||
|
update: () => true,
|
||||||
|
remove: () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
Industries.schema = new SimpleSchema({
|
||||||
|
geo: LocationSchema,
|
||||||
|
name: { type: String, optional: true },
|
||||||
|
registry: String
|
||||||
|
});
|
||||||
|
|
||||||
|
Industries.attachSchema(Industries.schema);
|
||||||
|
|
||||||
|
export const industriesRemap = (odoc) => {
|
||||||
|
const doc = odoc;
|
||||||
|
const geo = doc.geo;
|
||||||
|
doc.lat = geo.coordinates[1];
|
||||||
|
doc.lon = geo.coordinates[0];
|
||||||
|
doc.id = doc._id;
|
||||||
|
delete doc.geo;
|
||||||
|
return doc;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Industries;
|
||||||
29
imports/api/Industries/IndustryRegistries.js
Normal file
29
imports/api/Industries/IndustryRegistries.js
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* eslint-disable consistent-return */
|
||||||
|
|
||||||
|
import { Mongo } from 'meteor/mongo';
|
||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
|
||||||
|
const IndustryRegistries = new Mongo.Collection('industryregistries', { idGeneration: 'MONGO' });
|
||||||
|
|
||||||
|
IndustryRegistries.allow({
|
||||||
|
insert: () => false,
|
||||||
|
update: () => false,
|
||||||
|
remove: () => false
|
||||||
|
});
|
||||||
|
|
||||||
|
IndustryRegistries.deny({
|
||||||
|
insert: () => true,
|
||||||
|
update: () => true,
|
||||||
|
remove: () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
IndustryRegistries.schema = new SimpleSchema({
|
||||||
|
name: String,
|
||||||
|
agency: String,
|
||||||
|
region: String
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
IndustryRegistries.attachSchema(IndustryRegistries.schema);
|
||||||
|
|
||||||
|
export default IndustryRegistries;
|
||||||
|
|
@ -7,6 +7,8 @@ import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions'
|
||||||
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
||||||
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
|
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
|
||||||
import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
|
import FalsePositives from '/imports/api/FalsePositives/FalsePositives';
|
||||||
|
import Industries from '/imports/api/Industries/Industries';
|
||||||
|
import IndustryRegistries from '/imports/api/Industries/IndustryRegistries';
|
||||||
|
|
||||||
Meteor.startup(() => {
|
Meteor.startup(() => {
|
||||||
// https://github.com/percolatestudio/meteor-migrations
|
// https://github.com/percolatestudio/meteor-migrations
|
||||||
|
|
@ -127,6 +129,22 @@ Meteor.startup(() => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Migrations.add({
|
||||||
|
version: 9,
|
||||||
|
up: function siteSettingsAddIndex() {
|
||||||
|
Industries._ensureIndex({ registry: 1 });
|
||||||
|
Industries._ensureIndex({ geo: '2dsphere' });
|
||||||
|
// https://www.eea.europa.eu/data-and-maps/data/member-states-reporting-art-7-under-the-european-pollutant-release-and-transfer-register-e-prtr-regulation-16
|
||||||
|
IndustryRegistries.insert({
|
||||||
|
_id: '1', name: 'E-PRTR', agency: 'EEA', region: 'EU'
|
||||||
|
});
|
||||||
|
// https://www.epa.gov/enviro/epa-frs-facilities-state-single-file-csv-download
|
||||||
|
IndustryRegistries.insert({
|
||||||
|
_id: '2', name: 'FRS', agency: 'EPA', region: 'US'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Set createdAt in users & subs
|
// Set createdAt in users & subs
|
||||||
Migrations.migrateTo('latest');
|
Migrations.migrateTo('latest');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,40 @@
|
||||||
import React from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Circle } from 'react-leaflet';
|
import { Circle } from 'react-leaflet';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { onMarkClick } from './MarkListeners';
|
||||||
import FirePopup from './FirePopup';
|
import FirePopup from './FirePopup';
|
||||||
|
|
||||||
const FireCircleMark = ({
|
|
||||||
lat,
|
class FireCircleMark extends Component {
|
||||||
lon,
|
constructor(props) {
|
||||||
nasa,
|
super(props);
|
||||||
scan,
|
this.t = props.t;
|
||||||
id,
|
this.onClick = this.onClick.bind(this);
|
||||||
when,
|
}
|
||||||
history,
|
|
||||||
t
|
onClick() {
|
||||||
}) => (
|
const { history, nasa, id } = this.props;
|
||||||
<Circle center={[lat, lon]} color="red" stroke={false} fillOpacity="1" fill radius={scan * 1000}>
|
onMarkClick(history, nasa, id);
|
||||||
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
}
|
||||||
</Circle>
|
|
||||||
);
|
render() {
|
||||||
|
const {
|
||||||
|
lat,
|
||||||
|
lon,
|
||||||
|
scan,
|
||||||
|
nasa,
|
||||||
|
id,
|
||||||
|
history,
|
||||||
|
when,
|
||||||
|
t
|
||||||
|
} = this.props;
|
||||||
|
return (
|
||||||
|
<Circle center={[lat, lon]} color="red" stroke={false} fillOpacity="1" fill radius={scan * 500} onClick={this.onClick}>
|
||||||
|
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
||||||
|
</Circle>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FireCircleMark.propTypes = {
|
FireCircleMark.propTypes = {
|
||||||
scan: PropTypes.number.isRequired,
|
scan: PropTypes.number.isRequired,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
import React, { Component, Fragment } from 'react';
|
import React, { Component, Fragment } from 'react';
|
||||||
import { CircleMarker, Marker, Tooltip } from 'react-leaflet';
|
import { CircleMarker, Marker, Tooltip } from 'react-leaflet';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { fireIconS, fireIconM, fireIconL, nFireIcon, industryIcon } from '/imports/ui/components/Maps/Icons';
|
import { fireIconS, fireIconM, fireIconL, nFireIcon, industryIcon, regIndustryIcon } from '/imports/ui/components/Maps/Icons';
|
||||||
|
import { onMarkClick } from './MarkListeners';
|
||||||
import { translate } from 'react-i18next';
|
import { translate } from 'react-i18next';
|
||||||
import FirePopup from './FirePopup';
|
import FirePopup from './FirePopup';
|
||||||
|
|
||||||
|
|
@ -14,8 +15,8 @@ class FireIconMark extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick() {
|
onClick() {
|
||||||
// console.log('onClick fired');
|
const { history, nasa, id } = this.props;
|
||||||
this.props.history.push(`/fire/${this.props.nasa ? 'active' : 'alert'}/${this.props.id}`);
|
onMarkClick(history, nasa, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some docs:
|
// Some docs:
|
||||||
|
|
@ -36,22 +37,34 @@ class FireIconMark extends Component {
|
||||||
id,
|
id,
|
||||||
history,
|
history,
|
||||||
falsePositives,
|
falsePositives,
|
||||||
|
industries,
|
||||||
when,
|
when,
|
||||||
t
|
t
|
||||||
} = this.props;
|
} = this.props;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{ !falsePositives &&
|
{ !falsePositives && !industries &&
|
||||||
<Marker position={[lat, lon]} icon={nasa ? this.getIcon(scan) : nFireIcon} onClick={this.onClick} >
|
<Marker position={[lat, lon]} icon={nasa ? this.getIcon(scan) : nFireIcon} onClick={this.onClick} >
|
||||||
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
||||||
</Marker> }
|
</Marker> }
|
||||||
{ falsePositives &&
|
{ industries && <Marker position={[lat, lon]} icon={regIndustryIcon}>
|
||||||
|
<Tooltip><Fragment>{t('Es una industria (fuente: registro oficial)')}</Fragment></Tooltip>
|
||||||
|
</Marker> }
|
||||||
|
{ /* disabled */ industries && false && <CircleMarker
|
||||||
|
center={[lat, lon]}
|
||||||
|
color="violet"
|
||||||
|
stroke={false}
|
||||||
|
fillOpacity="1"
|
||||||
|
fill
|
||||||
|
radius={1}
|
||||||
|
/>}
|
||||||
|
{ falsePositives && !industries &&
|
||||||
<Marker position={[lat, lon]} icon={industryIcon} onClick={this.onClick}>
|
<Marker position={[lat, lon]} icon={industryIcon} onClick={this.onClick}>
|
||||||
<Tooltip><Fragment>{t('Es una industria')}</Fragment></Tooltip>
|
<Tooltip><Fragment>{t('Es una industria')}</Fragment></Tooltip>
|
||||||
{ /* disabled because was a past fire (and can be marked multiple times) */ false && <FirePopup t={t} history={history} id={id} lat={lat} lon={lon} /> }
|
{ /* disabled because was a past fire (and can be marked multiple times) */ false && <FirePopup t={t} history={history} id={id} lat={lat} lon={lon} /> }
|
||||||
</Marker>
|
</Marker>
|
||||||
}
|
}
|
||||||
{ !falsePositives &&
|
{ !falsePositives && !industries &&
|
||||||
<CircleMarker center={[lat, lon]} color={nasa ? 'red' : '#D35400'} stroke={false} fillOpacity="1" fill radius={1} onClick={this.onClick}>
|
<CircleMarker center={[lat, lon]} color={nasa ? 'red' : '#D35400'} stroke={false} fillOpacity="1" fill radius={1} onClick={this.onClick}>
|
||||||
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
<FirePopup t={t} history={history} id={id} nasa={nasa} lat={lat} lon={lon} when={when} />
|
||||||
</CircleMarker> }
|
</CircleMarker> }
|
||||||
|
|
@ -67,6 +80,7 @@ FireIconMark.propTypes = {
|
||||||
scan: PropTypes.number,
|
scan: PropTypes.number,
|
||||||
nasa: PropTypes.bool,
|
nasa: PropTypes.bool,
|
||||||
falsePositives: PropTypes.bool.isRequired,
|
falsePositives: PropTypes.bool.isRequired,
|
||||||
|
industries: PropTypes.bool.isRequired,
|
||||||
id: PropTypes.object.isRequired,
|
id: PropTypes.object.isRequired,
|
||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired,
|
||||||
when: PropTypes.instanceOf(Date),
|
when: PropTypes.instanceOf(Date),
|
||||||
|
|
|
||||||
|
|
@ -9,17 +9,17 @@ import FirePixel from '/imports/ui/components/Maps/FirePixel';
|
||||||
|
|
||||||
export default function FireList(props) {
|
export default function FireList(props) {
|
||||||
const {
|
const {
|
||||||
fires, scale, useMarkers, nasa, t, history, falsePositives
|
fires, scale, useMarkers, nasa, t, history, falsePositives, industries
|
||||||
} = props;
|
} = props;
|
||||||
const useMarks = useMarkers && scale;
|
const useMarks = useMarkers && scale;
|
||||||
const usePixel = !nasa || !scale;
|
const usePixel = !nasa || !scale;
|
||||||
/* console.log(`Using marks: ${useMarks}, using pixels: ${usePixel}`); */
|
/* console.log(`Using marks: ${useMarks}, using pixels: ${usePixel}`); */
|
||||||
let items;
|
let items;
|
||||||
if (useMarks) {
|
if (useMarks) {
|
||||||
items = fires.map(({ _id, ...otherProps }) => (<FireIconMark t={t} history={history} id={_id} key={_id} nasa={nasa} falsePositives={falsePositives} {...otherProps} />));
|
items = fires.map(({ _id, ...otherProps }) => (<FireIconMark t={t} history={history} id={_id} key={_id} nasa={nasa} falsePositives={falsePositives} industries={industries} {...otherProps} />));
|
||||||
} else if (usePixel && !falsePositives) {
|
} else if (usePixel && !falsePositives && !industries) {
|
||||||
items = fires.map(({ _id, ...otherProps }) => (<FirePixel t={t} key={_id} id={_id} history={history} nasa={nasa} {...otherProps} />));
|
items = fires.map(({ _id, ...otherProps }) => (<FirePixel t={t} key={_id} id={_id} history={history} nasa={nasa} {...otherProps} />));
|
||||||
} else if (!falsePositives) {
|
} else if (!falsePositives && !industries) {
|
||||||
items = fires.map(({ _id, ...otherProps }) => (<FireCircleMark t={t} history={history} id={_id} key={_id} nasa={nasa} {...otherProps} />));
|
items = fires.map(({ _id, ...otherProps }) => (<FireCircleMark t={t} history={history} id={_id} key={_id} nasa={nasa} {...otherProps} />));
|
||||||
}
|
}
|
||||||
return (<div style={{ display: 'none' }}>{items}</div>);
|
return (<div style={{ display: 'none' }}>{items}</div>);
|
||||||
|
|
@ -31,6 +31,7 @@ FireList.propTypes = {
|
||||||
useMarkers: PropTypes.bool.isRequired,
|
useMarkers: PropTypes.bool.isRequired,
|
||||||
nasa: PropTypes.bool.isRequired,
|
nasa: PropTypes.bool.isRequired,
|
||||||
falsePositives: PropTypes.bool.isRequired,
|
falsePositives: PropTypes.bool.isRequired,
|
||||||
|
industries: PropTypes.bool.isRequired,
|
||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired,
|
||||||
t: PropTypes.func.isRequired
|
t: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,12 @@ export const industryIcon = new Leaflet.Icon({
|
||||||
iconAnchor: [16, 20]
|
iconAnchor: [16, 20]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const regIndustryIcon = new Leaflet.Icon({
|
||||||
|
iconUrl: '/industry-marker-reg.png',
|
||||||
|
iconSize: [32, 37],
|
||||||
|
iconAnchor: [16, 20]
|
||||||
|
});
|
||||||
|
|
||||||
export const nFireIcon = new Leaflet.Icon({
|
export const nFireIcon = new Leaflet.Icon({
|
||||||
iconUrl: '/n-fire-marker.png',
|
iconUrl: '/n-fire-marker.png',
|
||||||
iconSize: [16, 24], // size of the icon
|
iconSize: [16, 24], // size of the icon
|
||||||
|
|
|
||||||
4
imports/ui/components/Maps/MarkListeners.js
Normal file
4
imports/ui/components/Maps/MarkListeners.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
export const onMarkClick = (history, nasa, id) => {
|
||||||
|
// console.log('onClick fired');
|
||||||
|
history.push(`/fire/${nasa ? 'active' : 'alert'}/${id}`);
|
||||||
|
};
|
||||||
|
|
@ -115,7 +115,8 @@ const App = props => (
|
||||||
<Authenticated exact path="/subscriptions/:_id/edit" component={EditSubscription} {...props} />
|
<Authenticated exact path="/subscriptions/:_id/edit" component={EditSubscription} {...props} />
|
||||||
<Authenticated exact path="/profile" component={Profile} {...props} />
|
<Authenticated exact path="/profile" component={Profile} {...props} />
|
||||||
<Authenticated exact path="/status" component={Status} {...props} />
|
<Authenticated exact path="/status" component={Status} {...props} />
|
||||||
<Route path="/fires" component={FiresMap} {...props} />
|
{/* <Route path="/fires/:type(with-industries)" component={FiresMap} industries {...props} /> */}
|
||||||
|
<Route path="/fires" component={FiresMap} industries={false} {...props} />
|
||||||
<Route path="/zones" component={ZonesMap} {...props} />
|
<Route path="/zones" component={ZonesMap} {...props} />
|
||||||
<Route path="/fire/:type(active|archive|alert)/:id" component={Fires} {...props} />
|
<Route path="/fire/:type(active|archive|alert)/:id" component={Fires} {...props} />
|
||||||
<Route path="/fire/:id" component={Fires} {...props} />
|
<Route path="/fire/:id" component={Fires} {...props} />
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { dateLongFormat } from '/imports/api/Common/dates';
|
||||||
import '/imports/startup/client/comments';
|
import '/imports/startup/client/comments';
|
||||||
import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes';
|
import FalsePositiveTypes from '/imports/api/FalsePositives/FalsePositiveTypes';
|
||||||
import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
|
import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
|
||||||
|
import IndustriesCollection, { industriesRemap } from '/imports/api/Industries/Industries';
|
||||||
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
|
import ShareIt from '/imports/ui/components/ShareIt/ShareIt';
|
||||||
import './Fires.scss';
|
import './Fires.scss';
|
||||||
|
|
||||||
|
|
@ -76,6 +77,7 @@ class Fire extends React.Component {
|
||||||
notfound, loading, fire, t
|
notfound, loading, fire, t
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
|
if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
|
||||||
|
if (Meteor.isDevelopment) console.log(`Industries total: ${this.props.industries.length}`);
|
||||||
/* console.log(`loading fire: ${loading}`);
|
/* console.log(`loading fire: ${loading}`);
|
||||||
* console.log(`Not found fire: ${notfound}`); */
|
* console.log(`Not found fire: ${notfound}`); */
|
||||||
if (fire && fire.when) {
|
if (fire && fire.when) {
|
||||||
|
|
@ -109,7 +111,7 @@ class Fire extends React.Component {
|
||||||
fillColor="red"
|
fillColor="red"
|
||||||
fillOpacity={0.0}
|
fillOpacity={0.0}
|
||||||
interactive={false}
|
interactive={false}
|
||||||
radius={fire.scan ? fire.scan * 1000 : 300}
|
radius={fire.scan ? fire.scan * 500 : 300}
|
||||||
ref={(circle) => { this.circle = circle; this.handleLeafletLoad(circle); }}
|
ref={(circle) => { this.circle = circle; this.handleLeafletLoad(circle); }}
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
@ -121,6 +123,17 @@ class Fire extends React.Component {
|
||||||
useMarkers
|
useMarkers
|
||||||
nasa={false}
|
nasa={false}
|
||||||
falsePositives
|
falsePositives
|
||||||
|
industries={false}
|
||||||
|
/>
|
||||||
|
<FireList
|
||||||
|
t={t}
|
||||||
|
history={this.props.history}
|
||||||
|
fires={this.props.industries}
|
||||||
|
scale
|
||||||
|
useMarkers
|
||||||
|
nasa={false}
|
||||||
|
falsePositives={false}
|
||||||
|
industries
|
||||||
/>
|
/>
|
||||||
<DefMapLayers satellite />
|
<DefMapLayers satellite />
|
||||||
</Map>
|
</Map>
|
||||||
|
|
@ -136,7 +149,7 @@ class Fire extends React.Component {
|
||||||
|
|
||||||
{(fire.type !== 'vecinal') &&
|
{(fire.type !== 'vecinal') &&
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{ this.props.falsePositives.length > 0 &&
|
{ (this.props.falsePositives.length > 0 || this.props.industries.length > 0) &&
|
||||||
<Row>
|
<Row>
|
||||||
<Col>
|
<Col>
|
||||||
<Alert bsStyle="success"><Trans>Parece que este fuego no es un fuego forestal.</Trans></Alert>
|
<Alert bsStyle="success"><Trans>Parece que este fuego no es un fuego forestal.</Trans></Alert>
|
||||||
|
|
@ -196,6 +209,7 @@ Fire.propTypes = {
|
||||||
loading: PropTypes.bool.isRequired,
|
loading: PropTypes.bool.isRequired,
|
||||||
notfound: PropTypes.bool.isRequired,
|
notfound: PropTypes.bool.isRequired,
|
||||||
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
|
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
industries: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
fromHash: PropTypes.bool.isRequired,
|
fromHash: PropTypes.bool.isRequired,
|
||||||
active: PropTypes.bool.isRequired,
|
active: PropTypes.bool.isRequired,
|
||||||
alert: PropTypes.bool.isRequired,
|
alert: PropTypes.bool.isRequired,
|
||||||
|
|
@ -236,12 +250,14 @@ const FireContainer = withTracker(({ match }) => {
|
||||||
/* console.log(`loading fire: ${loading}`);
|
/* console.log(`loading fire: ${loading}`);
|
||||||
* console.log(`Not found fire: ${notfound}`); */
|
* console.log(`Not found fire: ${notfound}`); */
|
||||||
const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
|
const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
|
||||||
|
const industries = IndustriesCollection.find().fetch().map(industriesRemap);
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
active,
|
active,
|
||||||
alert,
|
alert,
|
||||||
fromHash,
|
fromHash,
|
||||||
falsePositives,
|
falsePositives,
|
||||||
|
industries,
|
||||||
fire: FiresCollection.findOne(),
|
fire: FiresCollection.findOne(),
|
||||||
notfound,
|
notfound,
|
||||||
when: subscription.ready() && FiresCollection.findOne() ? FiresCollection.findOne().when : null
|
when: subscription.ready() && FiresCollection.findOne() ? FiresCollection.findOne().when : null
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import DefMapLayers from '/imports/ui/components/Maps/DefMapLayers';
|
||||||
import FromNow from '/imports/ui/components/FromNow/FromNow';
|
import FromNow from '/imports/ui/components/FromNow/FromNow';
|
||||||
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
|
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
|
||||||
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
|
||||||
|
import IndustriesCollection, { industriesRemap } from '/imports/api/Industries/Industries';
|
||||||
import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
|
import FalsePositivesCollection, { falsePositivesRemap } from '/imports/api/FalsePositives/FalsePositives';
|
||||||
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
|
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
|
||||||
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
|
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
|
||||||
|
|
@ -187,6 +188,8 @@ class FiresMap extends React.Component {
|
||||||
|
|
||||||
if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
|
if (Meteor.isDevelopment) console.log(`False positives total: ${this.props.falsePositives.length}`);
|
||||||
|
|
||||||
|
if (Meteor.isDevelopment) console.log(`Industries total: ${this.props.industries.length}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
/* Large number of markers:
|
/* Large number of markers:
|
||||||
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
|
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
|
||||||
|
|
@ -270,6 +273,17 @@ class FiresMap extends React.Component {
|
||||||
useMarkers={this.state.useMarkers}
|
useMarkers={this.state.useMarkers}
|
||||||
nasa={false}
|
nasa={false}
|
||||||
falsePositives
|
falsePositives
|
||||||
|
industries={false}
|
||||||
|
/>
|
||||||
|
<FireList
|
||||||
|
t={t}
|
||||||
|
history={this.props.history}
|
||||||
|
fires={this.props.industries}
|
||||||
|
scale={this.state.viewport.zoom >= MAXZOOM}
|
||||||
|
useMarkers={this.state.useMarkers}
|
||||||
|
nasa={false}
|
||||||
|
falsePositives={false}
|
||||||
|
industries
|
||||||
/>
|
/>
|
||||||
<FireList
|
<FireList
|
||||||
t={t}
|
t={t}
|
||||||
|
|
@ -279,6 +293,7 @@ class FiresMap extends React.Component {
|
||||||
useMarkers={this.state.useMarkers}
|
useMarkers={this.state.useMarkers}
|
||||||
nasa
|
nasa
|
||||||
falsePositives={false}
|
falsePositives={false}
|
||||||
|
industries={false}
|
||||||
/>
|
/>
|
||||||
<FireList
|
<FireList
|
||||||
t={t}
|
t={t}
|
||||||
|
|
@ -288,6 +303,7 @@ class FiresMap extends React.Component {
|
||||||
useMarkers={this.state.useMarkers}
|
useMarkers={this.state.useMarkers}
|
||||||
nasa={false}
|
nasa={false}
|
||||||
falsePositives={false}
|
falsePositives={false}
|
||||||
|
industries={false}
|
||||||
/>
|
/>
|
||||||
</Fragment> }
|
</Fragment> }
|
||||||
<DefMapLayers gray />
|
<DefMapLayers gray />
|
||||||
|
|
@ -318,6 +334,7 @@ FiresMap.propTypes = {
|
||||||
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
|
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
|
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
|
falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
industries: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
lastCheck: PropTypes.instanceOf(Date),
|
lastCheck: PropTypes.instanceOf(Date),
|
||||||
activefirestotal: PropTypes.number.isRequired,
|
activefirestotal: PropTypes.number.isRequired,
|
||||||
center: PropTypes.arrayOf(PropTypes.number),
|
center: PropTypes.arrayOf(PropTypes.number),
|
||||||
|
|
@ -331,6 +348,10 @@ FiresMap.propTypes = {
|
||||||
let geoInit = true;
|
let geoInit = true;
|
||||||
|
|
||||||
export default translate([], { wait: true })(withTracker(() => {
|
export default translate([], { wait: true })(withTracker(() => {
|
||||||
|
// const firesType = match.params.type;
|
||||||
|
// const withIndustries = firesType === 'with-industries';
|
||||||
|
// console.log(`With industries: ${withIndustries}`);
|
||||||
|
|
||||||
let subscription;
|
let subscription;
|
||||||
let alertSubscription;
|
let alertSubscription;
|
||||||
|
|
||||||
|
|
@ -339,6 +360,7 @@ export default translate([], { wait: true })(withTracker(() => {
|
||||||
const marksStored = store.get('firesmap_marks');
|
const marksStored = store.get('firesmap_marks');
|
||||||
const showUnionStored = store.get('firesmap_showunion');
|
const showUnionStored = store.get('firesmap_showunion');
|
||||||
zoom.set(zoomStored || 8);
|
zoom.set(zoomStored || 8);
|
||||||
|
center.set(centerStored || [0, 0]);
|
||||||
if (typeof marksStored === 'boolean') {
|
if (typeof marksStored === 'boolean') {
|
||||||
marks.set(marksStored);
|
marks.set(marksStored);
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +368,7 @@ export default translate([], { wait: true })(withTracker(() => {
|
||||||
showUnion.set(showUnionStored);
|
showUnion.set(showUnionStored);
|
||||||
}
|
}
|
||||||
Meteor.autorun(() => {
|
Meteor.autorun(() => {
|
||||||
if ((centerStored || geolocation.get()) && geoInit) {
|
if ((centerStored !== [0, 0] || geolocation.get()) && geoInit) {
|
||||||
center.set(centerStored || geolocation.get());
|
center.set(centerStored || geolocation.get());
|
||||||
// console.log(`Geolocation ${geolocation.get()}`);
|
// console.log(`Geolocation ${geolocation.get()}`);
|
||||||
geoInit = false;
|
geoInit = false;
|
||||||
|
|
@ -367,16 +389,28 @@ export default translate([], { wait: true })(withTracker(() => {
|
||||||
mapSize.get()[1].lng,
|
mapSize.get()[1].lng,
|
||||||
mapSize.get()[1].lat
|
mapSize.get()[1].lat
|
||||||
);
|
);
|
||||||
|
/* if (withIndustries) {
|
||||||
|
Meteor.subscribe(
|
||||||
|
'industriesMyloc',
|
||||||
|
mapSize.get()[0].lng,
|
||||||
|
mapSize.get()[0].lat,
|
||||||
|
mapSize.get()[1].lng,
|
||||||
|
mapSize.get()[1].lat
|
||||||
|
);
|
||||||
|
} */
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.subscribe('activefirestotal');
|
Meteor.subscribe('activefirestotal');
|
||||||
|
|
||||||
const settingsSubs = Meteor.subscribe('settings');
|
const settingsSubs = Meteor.subscribe('settings');
|
||||||
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
|
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
|
||||||
const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
|
const userSubs = SiteSettings.findOne({ name: 'subs-public-union' });
|
||||||
const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
|
const userSubsBounds = SiteSettings.findOne({ name: 'subs-public-union-bounds' });
|
||||||
const fireAlerts = FireAlertsCollection.find().fetch();
|
const fireAlerts = FireAlertsCollection.find().fetch();
|
||||||
const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
|
const falsePositives = FalsePositivesCollection.find().fetch().map(falsePositivesRemap);
|
||||||
|
const industries = IndustriesCollection.find().fetch().map(industriesRemap);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready() && alertSubscription.ready()),
|
loading: !subscription ? true : !(subscription.ready() && settingsSubs.ready() && alertSubscription.ready()),
|
||||||
userSubs: userSubs ? userSubs.value : null,
|
userSubs: userSubs ? userSubs.value : null,
|
||||||
|
|
@ -387,6 +421,7 @@ export default translate([], { wait: true })(withTracker(() => {
|
||||||
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
|
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
|
||||||
firealerts: fireAlerts,
|
firealerts: fireAlerts,
|
||||||
falsePositives,
|
falsePositives,
|
||||||
|
industries,
|
||||||
lastCheck: lastCheck ? lastCheck.value : null,
|
lastCheck: lastCheck ? lastCheck.value : null,
|
||||||
center: center.get(),
|
center: center.get(),
|
||||||
marks: marks.get(),
|
marks: marks.get(),
|
||||||
|
|
|
||||||
BIN
public/industry-marker-reg.png
Normal file
BIN
public/industry-marker-reg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 360 B |
|
|
@ -217,5 +217,7 @@
|
||||||
"About '{{appName}}'",
|
"About '{{appName}}'",
|
||||||
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
|
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
|
||||||
"Neighborhoods monitoring and fighting fires? What is all this about?",
|
"Neighborhoods monitoring and fighting fires? What is all this about?",
|
||||||
"noActiveNeigFireInMap": "There are no fires recently reported by our users in this area."
|
"noActiveNeigFireInMap": "There are no fires recently reported by our users in this area.",
|
||||||
|
"Es una industria (fuente: registro oficial)":
|
||||||
|
"It's an industry (source: oficial register)"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -304,5 +304,7 @@
|
||||||
"Sobre '{{appName}}'",
|
"Sobre '{{appName}}'",
|
||||||
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
|
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?":
|
||||||
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?",
|
"¿Vecindarios vigilando y combatiendo fuegos? ¿de qué va todo esto?",
|
||||||
"noActiveNeigFireInMap": "No hay fuegos notificados recientemente por nuestros usuarios/as en esta zona."
|
"noActiveNeigFireInMap": "No hay fuegos notificados recientemente por nuestros usuarios/as en esta zona.",
|
||||||
|
"Es una industria (fuente: registro oficial)":
|
||||||
|
"Es una industria (fuente: registro oficial)"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue