Better publication and count of active fires in current map

This commit is contained in:
vjrj 2018-01-30 18:05:11 +01:00
parent 96595b3aa4
commit 2fec833aef
4 changed files with 111 additions and 128 deletions

View file

@ -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);
}); });

View file

@ -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;
}); });

View 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;
});
}

View file

@ -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()