diff --git a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js index 167bcfa..ebcd9a0 100644 --- a/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js +++ b/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js @@ -1,27 +1,28 @@ +/* global Geolocation */ import React from 'react'; -import './CenterInMyPosition.scss'; +import PropTypes from 'prop-types'; +import { Tracker } from 'meteor/tracker'; +import { ReactiveVar } from 'meteor/reactive-var'; import { Button } from 'react-bootstrap'; import { translate } from 'react-i18next'; +import './CenterInMyPosition.scss'; class CenterInMyPosition extends React.Component { - constructor(props) { - super(props); - } - - onClick = (event) => { + onClick() { // this.props.onClick(event); // https://atmospherejs.com/mdg/geolocation // only with SSL: // https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition + console.log('Center'); // https://stackoverflow.com/questions/31608579/somethings-wrong-with-my-meteor-geolocation-functions - var userGeoLocation = new ReactiveVar(null); - var self = this; - Tracker.autorun(function (computation) { + const userGeoLocation = new ReactiveVar(null); + const self = this; + Tracker.autorun((computation) => { userGeoLocation.set(Geolocation.latLng()); if (userGeoLocation.get()) { - //stop the tracker if we got something - var viewport = { + // stop the tracker if we got something + const viewport = { center: [userGeoLocation.get().lat, userGeoLocation.get().lng], zoom: 11 }; @@ -35,11 +36,14 @@ class CenterInMyPosition extends React.Component { render() { return ( - - ) + ); } } -export default translate([], { wait: true }) (CenterInMyPosition); +CenterInMyPosition.propTypes = { + t: PropTypes.func.isRequired +}; + +export default translate([], { wait: true })(CenterInMyPosition); diff --git a/imports/ui/pages/FiresMap/FiresMap.js b/imports/ui/pages/FiresMap/FiresMap.js index 1023c3e..b2ed7eb 100644 --- a/imports/ui/pages/FiresMap/FiresMap.js +++ b/imports/ui/pages/FiresMap/FiresMap.js @@ -1,97 +1,123 @@ -import React, {Component} from 'react'; -import { Row, Col, Checkbox } from 'react-bootstrap'; +/* global L Counter */ +/* eslint-disable import/no-absolute-path */ +/* eslint-disable react/jsx-indent-props */ +/* eslint-disable react/jsx-indent */ +import React from 'react'; import PropTypes from 'prop-types'; +import { Row, Col, Checkbox } from 'react-bootstrap'; import { Meteor } from 'meteor/meteor'; -import { Trans, translate } from 'react-i18next'; -import 'leaflet/dist/leaflet.css'; -import { Circle, CircleMarker, Map, Marker, Popup, TileLayer, PropTypes as MapPropTypes } from 'react-leaflet'; -import ActiveFiresCollection from '../../../api/ActiveFires/ActiveFires'; -import FireAlertsCollection from '../../../api/FireAlerts/FireAlerts'; -import UserSubsToFiresCollection from '../../../api/Subscriptions/Subscriptions'; -import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js'; +import { ReactiveVar } from 'meteor/reactive-var'; import { withTracker } from 'meteor/react-meteor-data'; -import 'leaflet-sleep/Leaflet.Sleep.js'; -import Loading from '../../components/Loading/Loading'; -import './FiresMap.scss'; +import { Trans, translate } from 'react-i18next'; +import { Circle, CircleMarker, Map, Marker, TileLayer, PropTypes as MapPropTypes } from 'react-leaflet'; import Leaflet from 'leaflet'; import LGeo from 'leaflet-geodesy'; import union from '@turf/union'; +import _ from 'lodash'; +import 'leaflet/dist/leaflet.css'; import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css'; import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js'; -import _ from 'lodash'; +import 'leaflet-sleep/Leaflet.Sleep.js'; +import geolocation from '/imports/startup/client/geolocation'; +import CenterInMyPosition from '/imports/ui/components/CenterInMyPosition/CenterInMyPosition.js'; +import ActiveFiresCollection from '../../../api/ActiveFires/ActiveFires'; +import FireAlertsCollection from '../../../api/FireAlerts/FireAlerts'; +import UserSubsToFiresCollection from '../../../api/Subscriptions/Subscriptions'; +import Loading from '../../components/Loading/Loading'; +import './FiresMap.scss'; + +const DEF_LAT = 35.159028; +const DEF_LNG = -46.738057; +const DEFAULT_VIEWPORT = { + center: [DEF_LAT, DEF_LNG], // a point in the sea + zoom: 8 +}; +const zoom = new ReactiveVar(8); +const lat = new ReactiveVar(DEF_LAT); +const lng = new ReactiveVar(DEF_LNG); +const height = new ReactiveVar(400); +const width = new ReactiveVar(400); // https://stackoverflow.com/questions/35394577/leaflet-js-union-merge-circles function unify(polyList) { - for (var i = 0; i < polyList.length; ++i) { - if (i == 0) { - var unionTemp = polyList[i].toGeoJSON(); + let unionTemp; + for (let i = 0; i < polyList.length; i += 1) { + if (i === 0) { + unionTemp = polyList[i].toGeoJSON(); } else { - unionTemp =union(unionTemp, polyList[i].toGeoJSON()); + unionTemp = union(unionTemp, polyList[i].toGeoJSON()); } } return L.geoJson(unionTemp); } const fireIcon = new Leaflet.Icon({ - iconUrl: "/fire-marker.png", + iconUrl: '/fire-marker.png', /* shadowUrl: require('../public/marker-shadow.png'), */ - iconSize: [16, 24], // size of the icon + iconSize: [16, 24], // size of the icon /* shadowSize: [50, 64], // size of the shadow */ - iconAnchor: [8, 26] // point of the icon which will correspond to marker's location + iconAnchor: [8, 26] // point of the icon which will correspond to marker's location /* shadowAnchor: [4, 62], // the same for the shadow - * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/ + * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor */ }); const nFireIcon = new Leaflet.Icon({ - iconUrl: "/n-fire-marker.png", + iconUrl: '/n-fire-marker.png', /* shadowUrl: require('../public/marker-shadow.png'), */ - iconSize: [16, 24], // size of the icon + iconSize: [16, 24], // size of the icon /* shadowSize: [50, 64], // size of the shadow */ - iconAnchor: [8, 26] // point of the icon which will correspond to marker's location + iconAnchor: [8, 26] // point of the icon which will correspond to marker's location /* shadowAnchor: [4, 62], // the same for the shadow - * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/ + * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor */ }); // http://leafletjs.com/reference-1.2.0.html#icon -const MyPopupMarker = ({ children, lat, lon, nasa}) => ( -
- {/* - {children} - */} - - +const MyPopupMarker = ({ + lat, + lon, + nasa +}) => ( +
+ + {/* + {children} + */} + +
); -const FireMark = ({ lat, lon, scan, nasa }) => ( - +const FireMark = ({ + lat, + lon, + scan +}) => ( + ); /* Less acurate (1 pixel per fire) but faster */ const FirePixel = ({ lat, lon, nasa }) => ( - + ); MyPopupMarker.propTypes = { // https://github.com/PaulLeCam/react-leaflet/tree/master/src/propTypes children: MapPropTypes.children, lat: PropTypes.number.isRequired, - lon: PropTypes.number.isRequired, - nasa: PropTypes.bool.isRequired + lon: PropTypes.number.isRequired, + nasa: PropTypes.bool.isRequired }; FirePixel.propTypes = { lat: PropTypes.number.isRequired, - lon: PropTypes.number.isRequired, - nasa: PropTypes.bool.isRequired + lon: PropTypes.number.isRequired, + nasa: PropTypes.bool.isRequired }; FireMark.propTypes = { scan: PropTypes.number.isRequired, lat: PropTypes.number.isRequired, - lon: PropTypes.number.isRequired, - nasa: PropTypes.bool.isRequired + lon: PropTypes.number.isRequired }; // Below this use only pixels @@ -104,32 +130,25 @@ const MyMarkersList = ({ markers }) => { return
{items}
; }; -const FireList = ({ fires, scale, useMarkers, nasa }) => { - /* if (nasa) { - * console.log(`Scale: ${scale}`); - * for (var i = 0; i < fires.length; i ++) { - * console.log(fires[i].scan); - * } - * }*/ - const items = fires.map(({ _id, ...props }) => ( - useMarkers && scale? : - (!nasa && !scale)? :)); - return
{items}
; -}; +const FireList = ({ + fires, + scale, + useMarkers, + nasa }) => { + const items = fires.map(({ _id, ...props }) => ( + (useMarkers && scale) ? + : + (!nasa && !scale) ? + : + )); + return
{items}
; + }; MyMarkersList.propTypes = { markers: PropTypes.array.isRequired }; -const DEF_LAT = 35.159028; -const DEF_LNG = -46.738057; -const DEFAULT_VIEWPORT = { - center: [DEF_LAT, DEF_LNG], // a point in the sea - zoom: 8 -}; - class FiresMap extends React.Component { - constructor(props) { super(props); this.state = { @@ -139,57 +158,63 @@ class FiresMap extends React.Component { showSubsUnion: true }; this.unionGroup = new L.LayerGroup(); - } - - centerOnUserLocation = (viewport) => { - this.handleViewportChange(viewport); - }; - - handleViewportChange = function (viewport) { - console.log(`Viewport changed: ${JSON.stringify(viewport)}`); - zoom.set(viewport.zoom); - lat.set(viewport.center[0]); - lng.set(viewport.center[1]); - this.state.viewport = viewport; - this.state.modified = true; - if (this.props.subsready && this.refs.fireMap) { - this.showSubsUnion(this.state.showSubsUnion); - } + const self = this; + // viewportchange + // https://stackoverflow.com/questions/23123138/perform-debounce-in-react-js + this.debounceView = _.debounce((viewport) => { + self.handleViewportChange(viewport); + }, 2000); + this.onViewportChanged = this.onViewportChanged.bind(this); } componentDidMount() { height.set(this.divElement.clientHeight); width.set(this.divElement.clientWidth); this.addScale(); - const self = this; - // viewportchange - this.debounceView = _.debounce(function(viewport) {this.handleViewportChange(viewport); }.bind(this), 2000); } - // https://stackoverflow.com/questions/23123138/perform-debounce-in-react-js - onViewportChanged = (viewport) => { + onViewportChanged(viewport) { this.debounceView(viewport); - }; + } - onClickReset = () => { + onClickReset() { // console.log("onclick"); // this.setState({ viewport: DEFAULT_VIEWPORT }) } - useMarkers = (use) => { + getMap() { + return this.fireMap.leafletElement; + } + + handleViewportChange(viewport) { + console.log(`Viewport changed: ${JSON.stringify(viewport)}`); + zoom.set(viewport.zoom); + lat.set(viewport.center[0]); + lng.set(viewport.center[1]); + this.setState({ viewport, modified: true }); + /* this.state.viewport = viewport; + * this.state.modified = true; */ + if (this.props.subsready && this.fireMap) { + this.showSubsUnion(this.state.showSubsUnion); + } + } + + centerOnUserLocation(viewport) { + this.handleViewportChange(viewport); + } + + useMarkers(use) { + /* this.setState({ userMarkers: use });*/ this.state.useMarkers = use; - this.forceUpdate(); + // this.forceUpdate(); } - getMap = () => { - return this.refs['fireMap'].leafletElement; - } - - showSubsUnion = (show) => { + showSubsUnion(show) { + // this.setState({ showSubsUnion: show }); this.state.showSubsUnion = show; const map = this.getMap(); // http://leafletjs.com/reference-1.2.0.html#layergroup - var unionGroup = this.unionGroup; + const self = this; if (this.union) { map.removeLayer(this.union); @@ -197,88 +222,92 @@ class FiresMap extends React.Component { if (show) { // http://leafletjs.com/reference-1.2.0.html#path - var copts = { + const copts = { parts: 144 }; - UserSubsToFiresCollection.find().forEach( function(subs){ - var circle = LGeo.circle([subs.lat, subs.lon], subs.distance * 1000, copts); - circle.addTo(unionGroup); + UserSubsToFiresCollection.find().forEach((subs) => { + const circle = LGeo.circle([subs.lat, subs.lon], subs.distance * 1000, copts); + circle.addTo(self.unionGroup); }); - this.union = unify(unionGroup.getLayers()); + this.union = unify(self.unionGroup.getLayers()); this.union.setStyle({ - color: "#145A32", - fillColor: "green", - fillOpacity: .1 + color: '#145A32', + fillColor: 'green', + fillOpacity: 0.1 }); this.union.addTo(map); } } - addScale = () => { + addScale() { // https://www.npmjs.com/package/leaflet-graphicscale const map = this.getMap(); - var options = { + const options = { fill: 'fill', showSubunits: true }; - var graphicScale = L.control.graphicScale([options]).addTo(map); + L.control.graphicScale([options]).addTo(map); } render() { this.state.viewport = !this.state.modified && this.props.viewport && Array.isArray(this.props.viewport.center)? this.props.viewport: this.state.viewport; - if (this.props.subsready && this.refs.fireMap) { + if (this.props.subsready && this.fireMap) { // Show union of users this.showSubsUnion(this.state.showSubsUnion); - }; + } return ( /* Large number of markers: https://stackoverflow.com/questions/43015854/large-dataset-of-markers-or-dots-in-leaflet/43019740#43019740 */
this.divElement = divElement} + ref={(divElement) => { this.divElement = divElement; }} > {this.props.loading ? - :""} + : ''}

Fuegos activos

- {this.props.activefires.length === 0? - No hay fuegos activos en esta zona del mapa. Hay un total de {{countTotal: this.props.activefirestotal}} fuegos activos detectados en todo el mundo.:En rojo, {{count: this.props.activefires.length}} fuegos activos en el mapa. Hay un total de {{countTotal: this.props.activefirestotal}} fuegos activos detectados en todo el mundo por la NASA. + { this.props.activefires.length === 0 ? + No hay fuegos activos en esta zona del mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo. : + En rojo, {{ count: this.props.activefires.length }} fuegos activos en el mapa. Hay un total de {{ countTotal: this.props.activefirestotal }} fuegos activos detectados en todo el mundo por la NASA. }

En naranja, los fuegos notificados por nuestros usuarios/as recientemente.

- + this.showSubsUnion(e.target.checked)}> Resaltar en verde el área vigilada por nuestros usuarios/as (*) - {(this.state.viewport.zoom >= MAXZOOM) && this.useMarkers(e.target.checked)}> + {(this.state.viewport.zoom >= MAXZOOM) && + this.useMarkers(e.target.checked)}> Resaltar los fuegos con un marcador - } - this.centerOnUserLocation(viewport)} /> + } + this.centerOnUserLocation(viewport)} />
{/* https://github.com/CliffCloud/Leaflet.Sleep */} - + { this.fireMap = map; }} + animate + minZoom={5} + preferCanvas + onClick={this.onClickReset} + viewport={this.state.viewport} + onViewportChanged={this.onViewportChanged} + sleep={window.location.pathname === '/'} + sleepTime={750} + wakeTime={750} + sleepNote + hoverToWake + wakeMessage={this.props.t('Pulsa para activar')} + sleepOpacity={0.6} + > {/* http://wiki.openstreetmap.org/wiki/Tile_servers */} = MAXZOOM} useMarkers={this.state.useMarkers} - nasa={true} + nasa /> -

(*)Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.

+

(*)Para preservar la privacidad de nuestros usuarios/as, los datos reflejados están aleatoriamente alterados y son solo orientativos.

); - }; -}; - -const zoom = new ReactiveVar(8); -const lat = new ReactiveVar(DEF_LAT); -const lng = new ReactiveVar(DEF_LNG); -const height = new ReactiveVar(400); -const width = new ReactiveVar(400); + } +} FiresMap.propTypes = { loading: PropTypes.bool.isRequired, subsready: PropTypes.bool.isRequired, activefires: PropTypes.arrayOf(PropTypes.object).isRequired, + firealerts: PropTypes.arrayOf(PropTypes.object).isRequired, activefirestotal: PropTypes.number.isRequired, - viewport: PropTypes.object.isRequired + viewport: PropTypes.object.isRequired, + t: PropTypes.func.isRequired }; -Meteor.call("geo", function (error, response) { - if (error) { - console.warn(error); - } else { - lat.set(response.location.latitude); - lng.set(response.location.longitude); - } -}); - -export default translate([], { wait: true }) (withTracker(() => { - var subscription; - Meteor.autorun(function() { +export default translate([], { wait: true })(withTracker(() => { + let subscription; + Meteor.autorun(() => { // Subscribe for the current templateId (only if one is selected). Note this // will automatically clean up any previously subscribed data and it will // also stop all subscriptions when this template is destroyed. + if (geolocation.get()) { + lat.set(geolocation.get()[0]); + lng.set(geolocation.get()[1]); + } if (zoom.get() || lat.get() || lng.get()) { subscription = Meteor.subscribe('activefiresmyloc', zoom.get(), lat.get(), lng.get(), height.get(), width.get()); } @@ -343,7 +363,7 @@ export default translate([], { wait: true }) (withTracker(() => { Meteor.subscribe('activefirestotal'); // Right now to all neighborhood alerts Meteor.subscribe('fireAlerts'); - var userSubs = Meteor.subscribe('userSubsToFires'); + const userSubs = Meteor.subscribe('userSubsToFires'); // const subscription = Meteor.subscribe('activefiresmyloc', zoom.get()); // console.log(`Active fires ${ActiveFiresCollection.find().fetch().length} of ${Counter.get('countActiveFires')}`); // console.log(`Active neighborhood fires ${FireAlertsCollection.find().fetch().length} and users subscribed ${UserSubsToFiresCollection.find().fetch().length}`); @@ -353,11 +373,12 @@ export default translate([], { wait: true }) (withTracker(() => { subsready: userSubs.ready(), activefires: ActiveFiresCollection.find().fetch(), activefirestotal: Counter.get('countActiveFires'), - firealerts: FireAlertsCollection.find().fetch().map( - doc => ( { _id: doc['_id'], lat: doc['location'].lat, lon: doc['location'].lon })), + firealerts: FireAlertsCollection.find().fetch().map(doc => ( + { _id: doc._id, lat: doc.location.lat, lon: doc.location.lon } + )), userSubs: UserSubsToFiresCollection.find().fetch(), viewport: { - center: [lat.get(), lng.get()], // a point in the sea + center: geolocation.get(), zoom: zoom.get() } };