Better publication and count of active fires in current map
This commit is contained in:
parent
96595b3aa4
commit
2fec833aef
4 changed files with 111 additions and 128 deletions
|
|
@ -3,8 +3,8 @@
|
||||||
/* eslint-disable prefer-arrow-callback */
|
/* eslint-disable prefer-arrow-callback */
|
||||||
|
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
import { check, Match } from 'meteor/check';
|
import { check } from 'meteor/check';
|
||||||
import { localize } from '/imports/startup/server/IPGeocoder';
|
import { NumberBetween } from '/imports/modules/server/other-checks';
|
||||||
import ActiveFires from '../ActiveFires';
|
import ActiveFires from '../ActiveFires';
|
||||||
|
|
||||||
const counter = new Counter('countActiveFires', ActiveFires.find({}));
|
const counter = new Counter('countActiveFires', ActiveFires.find({}));
|
||||||
|
|
@ -13,60 +13,14 @@ Meteor.publish('activefirestotal', function total() {
|
||||||
return counter;
|
return counter;
|
||||||
});
|
});
|
||||||
|
|
||||||
const validZoom = Match.Where((zoom) => {
|
const activefires = (northEastLng, northEastLat, southWestLng, southWestLat) => {
|
||||||
// http://wiki.openstreetmap.org/wiki/Zoom_levels
|
|
||||||
check(zoom, Number);
|
|
||||||
return zoom >= 0 && zoom <= 19;
|
|
||||||
});
|
|
||||||
|
|
||||||
// https://github.com/3stack-software/meteor-match-library
|
|
||||||
function NumberBetween(min, max) {
|
|
||||||
return Match.Where((x) => {
|
|
||||||
check(x, Number);
|
|
||||||
return min <= x && x <= max;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function NullOr(type) {
|
|
||||||
return Match.Where((x) => {
|
|
||||||
if (x === null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
check(x, type);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://wiki.openstreetmap.org/wiki/Zoom_levels
|
|
||||||
// http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Resolution_and_Scale
|
|
||||||
|
|
||||||
// http://cwestblog.com/2012/11/12/javascript-degree-and-radian-conversion/
|
|
||||||
Math.radians = (degrees) => {
|
|
||||||
const rad = (degrees * Math.PI) / 180;
|
|
||||||
return rad;
|
|
||||||
};
|
|
||||||
|
|
||||||
const activefires = (zoom, lat, lng, height, width) => {
|
|
||||||
// latitude -90 and 90 and the longitude between -180 and 180
|
|
||||||
check(lat, NumberBetween(-90, 90));
|
|
||||||
check(lng, NumberBetween(-180, 180));
|
|
||||||
|
|
||||||
// console.log("Zoom: " + zoom + " lat: " + lat + " lng: " + lng);
|
|
||||||
const resolution = (156543.03 * Math.cos(Math.radians(lat))) / (2 ** zoom);
|
|
||||||
const distUnt = resolution * Math.max(height, width);
|
|
||||||
const distance = Math.trunc(distUnt);
|
|
||||||
// console.log(`so ${height}x${width} gives ${Math.trunc(resolution*height/1000)} x ${Math.trunc(resolution*width/1000)} km, so looking in ${distance}`);
|
|
||||||
// console.log(`So ${height}x${width} gives ${Math.trunc(resolution)} of resolution, so looking in ${Math.trunc(distance / 1000)}km`);
|
|
||||||
|
|
||||||
const fires = ActiveFires.find({
|
const fires = ActiveFires.find({
|
||||||
ourid: {
|
ourid: {
|
||||||
$near: {
|
$geoWithin: {
|
||||||
$geometry: {
|
$box: [
|
||||||
type: 'Point',
|
[southWestLng, southWestLat],
|
||||||
coordinates: [lng, lat]
|
[northEastLng, northEastLat]
|
||||||
},
|
]
|
||||||
$minDistance: 0,
|
|
||||||
$maxDistance: distance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
|
|
@ -80,48 +34,12 @@ const activefires = (zoom, lat, lng, height, width) => {
|
||||||
return fires;
|
return fires;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Meteor.publish('activefiresmyloc', function activeInMyLoc(northEastLng, northEastLat, southWestLng, southWestLat) {
|
||||||
Meteor.publish('allActiveFires', function allActive() {
|
|
||||||
// latitude -90 and 90 and the longitude between -180 and 180
|
// 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));
|
||||||
|
|
||||||
const { latitude, longitude } = localize().location;
|
return activefires(northEastLng, northEastLat, southWestLng, southWestLat);
|
||||||
// console.log(`${latitude}, ${longitude}`);
|
|
||||||
check(latitude, NumberBetween(-90, 90));
|
|
||||||
check(longitude, NumberBetween(-180, 180));
|
|
||||||
// https://docs.meteor.com/api/collections.html#Mongo-Collection-find
|
|
||||||
return ActiveFires.find({
|
|
||||||
ourid: {
|
|
||||||
$near: {
|
|
||||||
$geometry: {
|
|
||||||
type: 'Point',
|
|
||||||
coordinates: [longitude, latitude]
|
|
||||||
},
|
|
||||||
$minDistance: 0,
|
|
||||||
$maxDistance: 1000 // 156412000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
fields: {
|
|
||||||
_id: 0,
|
|
||||||
lat: 1,
|
|
||||||
lon: 1,
|
|
||||||
scan: 1
|
|
||||||
},
|
|
||||||
maxTimeMs: 30000
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Meteor.publish('activefiresmyloc', function activeInMyLoc(zoom, lat, lng, height, width) {
|
|
||||||
check(zoom, validZoom);
|
|
||||||
check(lat, NullOr(Number));
|
|
||||||
check(lng, NullOr(Number));
|
|
||||||
check(height, NullOr(Number));
|
|
||||||
check(width, NullOr(Number));
|
|
||||||
// console.log(`Check active fires in ${lat},${lng} with zoom ${zoom} pixels in ${height}x${width} map`);
|
|
||||||
if (lat && lng) {
|
|
||||||
return activefires(zoom, lat, lng, height, width);
|
|
||||||
}
|
|
||||||
const geo = localize();
|
|
||||||
// console.log(`${location.latitude}, ${location.longitude}`);
|
|
||||||
return activefires(zoom, geo.location.latitude, geo.location.longitude, height, width);
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,42 @@
|
||||||
/* eslint-disable prefer-arrow-callback */
|
/* eslint-disable prefer-arrow-callback */
|
||||||
|
/* eslint-disable import/no-absolute-path */
|
||||||
|
|
||||||
import { Meteor } from 'meteor/meteor';
|
import { Meteor } from 'meteor/meteor';
|
||||||
|
import { check } from 'meteor/check';
|
||||||
|
import { NumberBetween } from '/imports/modules/server/other-checks';
|
||||||
|
import moment from 'moment';
|
||||||
import FireAlerts from '../FireAlerts';
|
import FireAlerts from '../FireAlerts';
|
||||||
|
|
||||||
Meteor.publish('fireAlerts', function fireAlerts() {
|
Meteor.publish('fireAlerts', function fireAlerts(northEastLng, northEastLat, southWestLng, southWestLat) {
|
||||||
return FireAlerts.find();
|
// 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));
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/29327222/mongodb-find-created-results-by-date-today/29327353
|
||||||
|
const start = new Date();
|
||||||
|
start.setHours(0, 0, 0, 0);
|
||||||
|
const end = new Date();
|
||||||
|
end.setHours(23, 59, 59, 999);
|
||||||
|
|
||||||
|
const fires = FireAlerts.find({
|
||||||
|
ourid: {
|
||||||
|
$geoWithin: {
|
||||||
|
$box: [
|
||||||
|
[southWestLng, southWestLat],
|
||||||
|
[northEastLng, northEastLat]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: { $gte: start, $lt: end }
|
||||||
|
}, {
|
||||||
|
fields: {
|
||||||
|
lat: 1,
|
||||||
|
lon: 1,
|
||||||
|
scan: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// console.log(`Fires total: ${fires.count()}`);
|
||||||
|
return fires;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
25
imports/modules/server/other-checks.js
Normal file
25
imports/modules/server/other-checks.js
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { check, Match } from 'meteor/check';
|
||||||
|
|
||||||
|
export const validZoom = Match.Where((zoom) => {
|
||||||
|
// http://wiki.openstreetmap.org/wiki/Zoom_levels
|
||||||
|
check(zoom, Number);
|
||||||
|
return zoom >= 0 && zoom <= 19;
|
||||||
|
});
|
||||||
|
|
||||||
|
// https://github.com/3stack-software/meteor-match-library
|
||||||
|
export function NumberBetween(min, max) {
|
||||||
|
return Match.Where((x) => {
|
||||||
|
check(x, Number);
|
||||||
|
return min <= x && x <= max;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NullOr(type) {
|
||||||
|
return Match.Where((x) => {
|
||||||
|
if (x === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
check(x, type);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -34,8 +34,8 @@ import './FiresMap.scss';
|
||||||
const MAXZOOM = 6;
|
const MAXZOOM = 6;
|
||||||
const MAXZOOMREACTIVE = 6;
|
const MAXZOOMREACTIVE = 6;
|
||||||
const zoom = new ReactiveVar(8);
|
const zoom = new ReactiveVar(8);
|
||||||
const center = new ReactiveVar([null, null]);
|
const center = new ReactiveVar([0, 0]);
|
||||||
const mapSize = new ReactiveVar([400, 400]);
|
const mapSize = new ReactiveVar();
|
||||||
|
|
||||||
// Remove map in subscription
|
// Remove map in subscription
|
||||||
class FiresMap extends React.Component {
|
class FiresMap extends React.Component {
|
||||||
|
|
@ -59,9 +59,12 @@ class FiresMap extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
mapSize.set([this.divElement.clientHeight, this.divElement.clientWidth]);
|
|
||||||
if (this.fireMap) {
|
if (this.fireMap) {
|
||||||
this.addScale();
|
if (this.getMap().getCenter()) {
|
||||||
|
const bounds = this.getMap().getBounds();
|
||||||
|
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
|
||||||
|
this.addScale();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -69,9 +72,6 @@ class FiresMap extends React.Component {
|
||||||
this.debounceView(viewport);
|
this.debounceView(viewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickReset() {
|
|
||||||
}
|
|
||||||
|
|
||||||
getMap() {
|
getMap() {
|
||||||
return this.fireMap.leafletElement;
|
return this.fireMap.leafletElement;
|
||||||
}
|
}
|
||||||
|
|
@ -82,6 +82,10 @@ class FiresMap extends React.Component {
|
||||||
|
|
||||||
handleViewportChange(viewport) {
|
handleViewportChange(viewport) {
|
||||||
console.log(`Viewport changed: ${JSON.stringify(viewport)}`);
|
console.log(`Viewport changed: ${JSON.stringify(viewport)}`);
|
||||||
|
const bounds = this.getMap().getBounds();
|
||||||
|
// console.log(bounds);
|
||||||
|
mapSize.set([bounds.getNorthEast(), bounds.getSouthWest()]);
|
||||||
|
/*
|
||||||
if (viewport.center === this.state.viewport.center &&
|
if (viewport.center === this.state.viewport.center &&
|
||||||
viewport.zoom === this.state.viewport.zoom) {
|
viewport.zoom === this.state.viewport.zoom) {
|
||||||
// Do nothing, in same point
|
// Do nothing, in same point
|
||||||
|
|
@ -89,7 +93,7 @@ class FiresMap extends React.Component {
|
||||||
}
|
}
|
||||||
zoom.set(viewport.zoom);
|
zoom.set(viewport.zoom);
|
||||||
center.set(viewport.center);
|
center.set(viewport.center);
|
||||||
this.setState({ viewport });
|
this.setState({ viewport }); */
|
||||||
}
|
}
|
||||||
|
|
||||||
centerOnUserLocation(viewport) {
|
centerOnUserLocation(viewport) {
|
||||||
|
|
@ -124,7 +128,7 @@ class FiresMap extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
console.log(`Rendering ${this.props.loading ? 'loading' : 'LOADED'} map ${this.props.activefires.length} of ${this.props.activefirestotal} total. Subs users ready ${this.props.subsready}, 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. Subs users ready ${this.props.subsready}, reactive ${this.state.viewport.zoom >= MAXZOOMREACTIVE}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
/* Large number of markers:
|
/* Large number of markers:
|
||||||
|
|
@ -141,9 +145,9 @@ class FiresMap extends React.Component {
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={12} sm={6} md={6} lg={6} >
|
<Col xs={12} sm={6} md={6} lg={6} >
|
||||||
<p className="firesmap-legend">
|
<p className="firesmap-legend">
|
||||||
{ this.props.activefires.length === 0 ?
|
{ (this.props.activefires.length + this.props.firealerts.length) === 0 ?
|
||||||
<Fragment><Trans parent="span" i18nKey="noActiveFireInMapCount">No hay fuegos activos en esta zona del mapa. Hay un total de <strong>{{ countTotal: this.props.activefirestotal }}</strong> fuegos activos detectados en todo el mundo.</Trans> <Trans>Datos actualizados <FromNow when={this.props.lastCheck} />.</Trans></Fragment> :
|
<Fragment><Trans parent="span" i18nKey="noActiveFireInMapCount">No hay fuegos activos en esta zona del mapa. Hay un total de <strong>{{ countTotal: this.props.activefirestotal }}</strong> fuegos activos detectados en todo el mundo.</Trans> <Trans>Datos actualizados <FromNow when={this.props.lastCheck} />.</Trans></Fragment> :
|
||||||
<Fragment><Trans parent="span" i18nKey="activeFireInMapCount">En rojo, <strong>{{ count: this.props.activefires.length }}</strong> fuegos activos en el mapa. Hay un total de <strong>{{ countTotal: this.props.activefirestotal }}</strong> fuegos activos detectados en todo el mundo por la NASA.</Trans> <Trans>Datos actualizados <FromNow when={this.props.lastCheck} />.</Trans></Fragment>
|
<Fragment><Trans parent="span" i18nKey="activeFireInMapCount">En rojo, <strong>{{ count: this.props.activefires.length + this.props.firealerts.length }}</strong> fuegos activos en el mapa. Hay un total de <strong>{{ countTotal: this.props.activefirestotal }}</strong> fuegos activos detectados en todo el mundo por la NASA.</Trans> <Trans>Datos actualizados <FromNow when={this.props.lastCheck} />.</Trans></Fragment>
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
{isNotHomeAndMobile &&
|
{isNotHomeAndMobile &&
|
||||||
|
|
@ -178,6 +182,8 @@ class FiresMap extends React.Component {
|
||||||
className="firesmap-leaflet-container"
|
className="firesmap-leaflet-container"
|
||||||
animate
|
animate
|
||||||
minZoom={5}
|
minZoom={5}
|
||||||
|
center={this.props.center}
|
||||||
|
zoom={this.props.zoom}
|
||||||
preferCanvas
|
preferCanvas
|
||||||
onClick={this.onClickReset}
|
onClick={this.onClickReset}
|
||||||
viewport={this.state.viewport}
|
viewport={this.state.viewport}
|
||||||
|
|
@ -237,47 +243,47 @@ FiresMap.propTypes = {
|
||||||
t: PropTypes.func.isRequired
|
t: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let init = true;
|
||||||
|
|
||||||
export default translate([], { wait: true })(withTracker(() => {
|
export default translate([], { wait: true })(withTracker(() => {
|
||||||
let subscription;
|
let subscription;
|
||||||
let init = true;
|
|
||||||
Meteor.autorun(() => {
|
Meteor.autorun(() => {
|
||||||
if (geolocation.get() && init) {
|
if (geolocation.get() && init) {
|
||||||
center.set(geolocation.get());
|
center.set(geolocation.get());
|
||||||
// console.log(`Geolocation ${geolocation.get()}`);
|
console.log(`Geolocation ${geolocation.get()}`);
|
||||||
init = false;
|
init = false;
|
||||||
}
|
}
|
||||||
if (mapSize.get()) {
|
if (mapSize.get() && mapSize.get()[0].lng && mapSize.get()[1].lat) {
|
||||||
subscription = Meteor.subscribe(
|
subscription = Meteor.subscribe(
|
||||||
'activefiresmyloc',
|
'activefiresmyloc',
|
||||||
zoom.get(),
|
mapSize.get()[0].lng,
|
||||||
center.get()[0],
|
mapSize.get()[0].lat,
|
||||||
center.get()[1],
|
mapSize.get()[1].lng,
|
||||||
mapSize.get()[0],
|
mapSize.get()[1].lat
|
||||||
mapSize.get()[1]
|
);
|
||||||
|
Meteor.subscribe(
|
||||||
|
'fireAlerts',
|
||||||
|
mapSize.get()[0].lng,
|
||||||
|
mapSize.get()[0].lat,
|
||||||
|
mapSize.get()[1].lng,
|
||||||
|
mapSize.get()[1].lat
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Meteor.subscribe('activefirestotal');
|
Meteor.subscribe('activefirestotal');
|
||||||
// Right now to all neighborhood alerts
|
|
||||||
Meteor.subscribe('fireAlerts');
|
|
||||||
Meteor.subscribe('settings');
|
Meteor.subscribe('settings');
|
||||||
const userSubs = Meteor.subscribe('userSubsToFires');
|
const userSubs = Meteor.subscribe('userSubsToFires');
|
||||||
// const subscription = Meteor.subscribe('activefiresmyloc', zoom.get());
|
|
||||||
// Warning with the performance of this log:
|
|
||||||
// console.log(`Active fires ${ActiveFiresCollection.find().count()} 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());
|
|
||||||
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
|
const lastCheck = SiteSettings.findOne({ name: 'last-fire-check' });
|
||||||
|
const fireAlerts = FireAlertsCollection.find().fetch();
|
||||||
return {
|
return {
|
||||||
loading: !subscription.ready(),
|
loading: !subscription ? true : !subscription.ready(),
|
||||||
userSubs: UserSubsToFiresCollection.find().fetch(),
|
userSubs: UserSubsToFiresCollection.find().fetch(),
|
||||||
subsready: userSubs.ready(),
|
subsready: userSubs.ready(),
|
||||||
// Not reactive query depending on zoom level
|
// Not reactive query depending on zoom level
|
||||||
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
|
activefires: ActiveFiresCollection.find({}, { reactive: zoom.get() >= MAXZOOMREACTIVE }).fetch(),
|
||||||
// activefires: ActiveFiresCollection.find({}).fetch(),
|
activefirestotal: Counter.get('countActiveFires') + fireAlerts.length,
|
||||||
activefirestotal: Counter.get('countActiveFires'),
|
firealerts: fireAlerts,
|
||||||
firealerts: FireAlertsCollection.find().fetch(),
|
|
||||||
lastCheck: lastCheck ? lastCheck.value : null,
|
lastCheck: lastCheck ? lastCheck.value : null,
|
||||||
center: geolocation.get(),
|
center: geolocation.get(),
|
||||||
zoom: zoom.get()
|
zoom: zoom.get()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue