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