import React, {Component} from 'react'; import { Row, Col, Checkbox } from 'react-bootstrap'; import PropTypes from 'prop-types'; 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 { withTracker } from 'meteor/react-meteor-data'; import 'leaflet-sleep/Leaflet.Sleep.js'; import Loading from '../../components/Loading/Loading'; import './FiresMap.scss'; import Leaflet from 'leaflet'; import LGeo from 'leaflet-geodesy'; import union from '@turf/union'; import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.css'; import 'leaflet-graphicscale/dist/Leaflet.GraphicScale.min.js'; import _ from 'lodash'; // 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(); } else { unionTemp =union(unionTemp, polyList[i].toGeoJSON()); } } return L.geoJson(unionTemp); } const fireIcon = new Leaflet.Icon({ iconUrl: "/fire-marker.png", /* shadowUrl: require('../public/marker-shadow.png'), */ 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 /* shadowAnchor: [4, 62], // the same for the shadow * popupAnchor: [-3, -76]// point from which the popup should open relative to the iconAnchor*/ }); const nFireIcon = new Leaflet.Icon({ iconUrl: "/n-fire-marker.png", /* shadowUrl: require('../public/marker-shadow.png'), */ 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 /* shadowAnchor: [4, 62], // the same for the shadow * 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 FireMark = ({ lat, lon, scan, nasa }) => ( ); /* 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 }; FirePixel.propTypes = { lat: PropTypes.number.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 }; // Below this use only pixels const MAXZOOM = 6; const MyMarkersList = ({ markers }) => { const items = markers.map(({ key, ...props }) => ( )); 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}
; }; 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 = { viewport: DEFAULT_VIEWPORT, modified: false, useMarkers: false, 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); } } 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) => { this.debounceView(viewport); }; onClickReset = () => { // console.log("onclick"); // this.setState({ viewport: DEFAULT_VIEWPORT }) } useMarkers = (use) => { this.state.useMarkers = use; this.forceUpdate(); } getMap = () => { return this.refs['fireMap'].leafletElement; } showSubsUnion = (show) => { this.state.showSubsUnion = show; const map = this.getMap(); // http://leafletjs.com/reference-1.2.0.html#layergroup var unionGroup = this.unionGroup; if (this.union) { map.removeLayer(this.union); } if (show) { // http://leafletjs.com/reference-1.2.0.html#path var copts = { parts: 144 }; UserSubsToFiresCollection.find().forEach( function(subs){ var circle = LGeo.circle([subs.lat, subs.lon], subs.distance * 1000, copts); circle.addTo(unionGroup); }); this.union = unify(unionGroup.getLayers()); this.union.setStyle({ color: "#145A32", fillColor: "green", fillOpacity: .1 }); this.union.addTo(map); } } addScale = () => { // https://www.npmjs.com/package/leaflet-graphicscale const map = this.getMap(); var options = { fill: 'fill', showSubunits: true }; var graphicScale = 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) { // 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} > {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. }

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)}> Resaltar los fuegos con un marcador } this.centerOnUserLocation(viewport)} />
{/* https://github.com/CliffCloud/Leaflet.Sleep */} {/* http://wiki.openstreetmap.org/wiki/Tile_servers */} = MAXZOOM} useMarkers={this.state.useMarkers} nasa={true} />

(*)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, activefirestotal: PropTypes.number.isRequired, viewport: PropTypes.object.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() { // 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 (zoom.get() || lat.get() || lng.get()) { subscription = Meteor.subscribe('activefiresmyloc', zoom.get(), lat.get(), lng.get(), height.get(), width.get()); } }); Meteor.subscribe('activefirestotal'); // Right now to all neighborhood alerts Meteor.subscribe('fireAlerts'); var 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}`); // console.log(UserSubsToFiresCollection.find().fetch()); return { loading: !subscription.ready(), 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 })), userSubs: UserSubsToFiresCollection.find().fetch(), viewport: { center: [lat.get(), lng.get()], // a point in the sea zoom: zoom.get() } }; })(FiresMap));