diff --git a/imports/api/FalsePositives/server/publications.js b/imports/api/FalsePositives/server/publications.js
new file mode 100644
index 0000000..9585907
--- /dev/null
+++ b/imports/api/FalsePositives/server/publications.js
@@ -0,0 +1,59 @@
+/* global Counter */
+/* eslint-disable import/no-absolute-path */
+/* eslint-disable prefer-arrow-callback */
+
+import { Meteor } from 'meteor/meteor';
+import { check } from 'meteor/check';
+import { NumberBetween } from '/imports/modules/server/other-checks';
+import FalsePositives from '../FalsePositives';
+
+const counter = new Counter('countFalsePositives', FalsePositives.find({}));
+
+Meteor.publish('falsePositivesTotal', function total() {
+ return counter;
+});
+
+const falsePositives = (northEastLng, northEastLat, southWestLng, southWestLat) => {
+ const fires = FalsePositives.find({
+ geo: {
+ $geoWithin: {
+ $box: [
+ [southWestLng, southWestLat],
+ [northEastLng, northEastLat]
+ ]
+ }
+ }
+ }, {
+ fields: {
+ geo: 1,
+ // type: 1,
+ // when: 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()}`);
+ return fires;
+};
+
+Meteor.publishTransformed('falsePositivesMyloc', function falsePositivesInMyLoc(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 falsePositives(northEastLng, northEastLat, southWestLng, southWestLat);
+});
diff --git a/imports/startup/server/api.js b/imports/startup/server/api.js
index 0fcde5d..73f1fa2 100644
--- a/imports/startup/server/api.js
+++ b/imports/startup/server/api.js
@@ -26,3 +26,4 @@ import '../../api/SiteSettings/methods';
import '../../api/SiteSettings/server/publications';
import '../../api/FalsePositives/methods';
+import '../../api/FalsePositives/server/publications';
diff --git a/imports/ui/components/Maps/FireIconMark.js b/imports/ui/components/Maps/FireIconMark.js
index f83b4bf..5082edf 100644
--- a/imports/ui/components/Maps/FireIconMark.js
+++ b/imports/ui/components/Maps/FireIconMark.js
@@ -2,7 +2,7 @@
import React from 'react';
import { CircleMarker, Marker } from 'react-leaflet';
import PropTypes from 'prop-types';
-import { fireIcon, nFireIcon } from '/imports/ui/components/Maps/Icons';
+import { fireIcon, nFireIcon, industryIcon } from '/imports/ui/components/Maps/Icons';
import { translate } from 'react-i18next';
import FirePopup from './FirePopup';
@@ -12,16 +12,24 @@ const FireIconMark = ({
nasa,
id,
history,
+ falsePositives,
when,
t
}) => (
-
-
-
-
-
-
+ { !falsePositives &&
+
+
+ }
+ { falsePositives &&
+
+ { /* disabled because was a past fire (and can be marked multiple times) */ false && }
+
+ }
+ { !falsePositives &&
+
+
+ }
);
@@ -29,10 +37,11 @@ FireIconMark.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
+ nasa: PropTypes.bool,
+ falsePositives: PropTypes.bool.isRequired,
id: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
- when: PropTypes.instanceOf(Date).isRequired,
+ when: PropTypes.instanceOf(Date),
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/FireList.js b/imports/ui/components/Maps/FireList.js
index be20855..f4a851c 100644
--- a/imports/ui/components/Maps/FireList.js
+++ b/imports/ui/components/Maps/FireList.js
@@ -9,17 +9,17 @@ import FirePixel from '/imports/ui/components/Maps/FirePixel';
export default function FireList(props) {
const {
- fires, scale, useMarkers, nasa, t, history
+ fires, scale, useMarkers, nasa, t, history, falsePositives
} = props;
const useMarks = useMarkers && scale;
const usePixel = !nasa || !scale;
/* console.log(`Using marks: ${useMarks}, using pixels: ${usePixel}`); */
let items;
if (useMarks) {
- items = fires.map(({ _id, ...otherProps }) => ());
- } else if (usePixel) {
+ items = fires.map(({ _id, ...otherProps }) => ());
+ } else if (usePixel && !falsePositives) {
items = fires.map(({ _id, ...otherProps }) => ());
- } else {
+ } else if (!falsePositives) {
items = fires.map(({ _id, ...otherProps }) => ());
}
return ({items}
);
@@ -30,6 +30,7 @@ FireList.propTypes = {
scale: PropTypes.bool.isRequired,
useMarkers: PropTypes.bool.isRequired,
nasa: PropTypes.bool.isRequired,
+ falsePositives: PropTypes.bool.isRequired,
history: PropTypes.object.isRequired,
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/FirePopup.js b/imports/ui/components/Maps/FirePopup.js
index c3f2f52..8e7d2a8 100644
--- a/imports/ui/components/Maps/FirePopup.js
+++ b/imports/ui/components/Maps/FirePopup.js
@@ -18,10 +18,11 @@ const FirePopup = ({
{t('Coordenadas:')} {lat}, {lon}
- {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')}
- {t('Detectado')}: {moment(when).fromNow()}
+ {nasa && {t('Fuente')}: {t(nasa ? 'NASA' : 'nuestros usuarios/as')}
}
+ {when && {t('Detectado')}: {moment(when).fromNow()}
}
- history.push(`/fire/active/${id}`)}>{t('Más información sobre este fuego')}
+ { /* if nasa === null means that the is a false positive fire */ }
+ history.push(`/fire/${nasa ? 'active' : 'archive'}/${id}`)}>{t('Más información sobre este fuego')}
@@ -33,10 +34,10 @@ FirePopup.propTypes = {
// https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes
lat: PropTypes.number.isRequired,
lon: PropTypes.number.isRequired,
- nasa: PropTypes.bool.isRequired,
+ nasa: PropTypes.bool,
id: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
- when: PropTypes.instanceOf(Date).isRequired,
+ when: PropTypes.instanceOf(Date),
t: PropTypes.func.isRequired
};
diff --git a/imports/ui/components/Maps/Icons.js b/imports/ui/components/Maps/Icons.js
index 7d8dc1f..2c04886 100644
--- a/imports/ui/components/Maps/Icons.js
+++ b/imports/ui/components/Maps/Icons.js
@@ -12,6 +12,12 @@ export const fireIcon = new Leaflet.Icon({
* popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor */
});
+export const industryIcon = new Leaflet.Icon({
+ iconUrl: '/industry-marker.png',
+ iconSize: [32, 37],
+ iconAnchor: [16, 20]
+});
+
export const nFireIcon = new Leaflet.Icon({
iconUrl: '/n-fire-marker.png',
iconSize: [16, 24], // size of the icon
diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js
index 91370e6..802948d 100644
--- a/imports/ui/pages/FiresMap/FiresMap.js
+++ b/imports/ui/pages/FiresMap/FiresMap.js
@@ -26,6 +26,7 @@ import FromNow from '/imports/ui/components/FromNow/FromNow';
import Loading from '/imports/ui/components/Loading/Loading';
import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires';
import FireAlertsCollection from '/imports/api/FireAlerts/FireAlerts';
+import FalsePositivesCollection from '/imports/api/FalsePositives/FalsePositives';
import SiteSettings from '/imports/api/SiteSettings/SiteSettings';
import UserSubsToFiresCollection from '/imports/api/Subscriptions/Subscriptions';
import { isNotHomeAndMobile, isChrome } from '/imports/ui/components/Utils/isMobile';
@@ -134,7 +135,10 @@ class FiresMap extends React.Component {
render() {
const { t } = this.props;
- console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length + this.props.firealerts.length} of ${this.props.activefirestotal} total. False positives: ${this.props.falsePositives.length}. Subs users ready ${this.props.subsready} (${this.props.userSubs.length}), reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
+ if (Meteor.isDevelopment) {
+ console.log(`False positives total: ${this.props.falsePositivesTotal}`);
+ }
return (
/* Large number of markers:
https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
@@ -210,6 +214,15 @@ class FiresMap extends React.Component {
{/* http://wiki.openstreetmap.org/wiki/Tile_servers */}
{!this.props.loading &&
+ = MAXZOOM}
+ useMarkers={this.state.useMarkers}
+ nasa={false}
+ falsePositives
+ />
= MAXZOOM}
useMarkers={this.state.useMarkers}
nasa
+ falsePositives={false}
/>
}
@@ -250,6 +265,8 @@ FiresMap.propTypes = {
userSubs: PropTypes.arrayOf(PropTypes.object).isRequired,
activefires: PropTypes.arrayOf(PropTypes.object).isRequired,
firealerts: PropTypes.arrayOf(PropTypes.object).isRequired,
+ falsePositives: PropTypes.arrayOf(PropTypes.object).isRequired,
+ falsePositivesTotal: PropTypes.number.isRequired,
lastCheck: PropTypes.instanceOf(Date),
activefirestotal: PropTypes.number.isRequired,
center: PropTypes.arrayOf(PropTypes.number),
@@ -283,14 +300,23 @@ export default translate([], { wait: true })(withTracker(() => {
mapSize.get()[1].lng,
mapSize.get()[1].lat
);
+ Meteor.subscribe(
+ 'falsePositivesMyloc',
+ mapSize.get()[0].lng,
+ mapSize.get()[0].lat,
+ mapSize.get()[1].lng,
+ mapSize.get()[1].lat
+ );
}
});
Meteor.subscribe('activefirestotal');
+ Meteor.subscribe('falsePositivesTotal');
Meteor.subscribe('settings');
const userSubs = Meteor.subscribe('userSubsToFires');
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
const fireAlerts = FireAlertsCollection.find().fetch();
+ const falsePositives = FalsePositivesCollection.find().fetch();
return {
loading: !subscription ? true : !subscription.ready(),
userSubs: UserSubsToFiresCollection.find().fetch(),
@@ -298,7 +324,9 @@ export default translate([], { wait: true })(withTracker(() => {
// Not reactive query depending on zoom level
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
+ falsePositivesTotal: Counter.get('countFalsePositives') + fireAlerts.length,
firealerts: fireAlerts,
+ falsePositives,
lastCheck: lastCheck ? lastCheck.value : null,
center: geolocation.get(),
zoom: zoom.get()
diff --git a/private/pages/credits-en.md b/private/pages/credits-en.md
index 049f3b5..185c5fd 100644
--- a/private/pages/credits-en.md
+++ b/private/pages/credits-en.md
@@ -12,6 +12,7 @@ We acknowledge the use of data and imagery from LANCE FIRMS operated by the NASA
* Home slide secondary 2: Smog over Almaty city, Kazakhstan, January 2014, photo by Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
* Wildfire home photo: [California Wildfires, October 23th 2007](https://commons.wikimedia.org/wiki/File:California_Wildfires_October_23_2007.jpg), [fuente](http://www.nasa.gov/vision/earth/lookingatearth/socal_wildfires_oct07.html).
* Default avatar: A colored Emoji from Noto project, released under Apache license, [source](https://commons.wikimedia.org/wiki/File:Emoji_u1f469_1f3fd_200d_1f692.svg).
+* Industry fire marker by Nicolas Mollet, released under CC-BY-SA 3.0, [source](https://mapicons.mapsmarker.com/category/markers/industry/).
**Others**
diff --git a/private/pages/credits-es.md b/private/pages/credits-es.md
index b1ada88..3673589 100644
--- a/private/pages/credits-es.md
+++ b/private/pages/credits-es.md
@@ -12,6 +12,7 @@ Reconocemos el uso de datos e imágenes de LANCE FIRMS operadas por NASA/GSFC/Ea
* Diapositiva secundaria 2 del Inicio: Polución en la ciudad de Almaty, Kazakhstan, enero de 2014, foto de Igors Jefimovs, CC-BY 3.0, [source](https://commons.wikimedia.org/wiki/File:Smog_over_Almaty.jpg)
* Foto de inicio de fuego forestal: [Incendios forestales de California, 23 de octubre de 2007](https://commons.wikimedia.org/wiki/File:California_Wildfires_October_23_2007.jpg), [fuente](http://www.nasa.gov/vision/earth/lookingatearth/socal_wildfires_oct07.html).
* Avatar predeterminado: un Emoji coloreado del proyecto Noto, publicado bajo licencia Apache, [fuente](https://commons.wikimedia.org/wiki/File:Emoji_u1f469_1f3fd_200d_1f692.svg).
+* Marcador de industia por Nicolas Mollet, publicado bajo CC-BY-SA 3.0, [fuente](https://mapicons.mapsmarker.com/category/markers/industry/).
**Otros**
diff --git a/public/industry-marker.png b/public/industry-marker.png
new file mode 100644
index 0000000..2babdab
Binary files /dev/null and b/public/industry-marker.png differ