From 2e36c9cd17a599025f98b9842a5c13a0afaf11c8 Mon Sep 17 00:00:00 2001 From: vjrj Date: Sun, 3 Dec 2017 16:54:24 +0100 Subject: [PATCH] Improved FiresMap (makers checkbox, better calcs, etc) --- .gitignore | 2 +- .meteor/packages | 1 - .meteor/versions | 1 - designs/icons-tcef.svg | 354 ++++++++++++++++++ .../api/ActiveFires/server/publications.js | 23 +- imports/startup/server/geolocation.js | 42 ++- imports/ui/layouts/App/App.js | 4 +- imports/ui/pages/FiresMap/FiresMap.js | 152 ++++---- imports/ui/pages/FiresMap/FiresMap.scss | 4 +- imports/ui/pages/Index/Index.js | 8 + package.json | 1 + public/fire-marker.png | Bin 0 -> 720 bytes public/locales/es/common.json | 8 +- 13 files changed, 496 insertions(+), 104 deletions(-) create mode 100644 designs/icons-tcef.svg create mode 100644 public/fire-marker.png diff --git a/.gitignore b/.gitignore index f5538d2..daedb47 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ settings-staging.json settings-production.json settings-development.json imports/ui/stylesheets/.sass-cache/ -public/GeoLite2-City.mmdb.gz +private/GeoLite2-City.mmdb* diff --git a/.meteor/packages b/.meteor/packages index 71a8dd1..e513970 100644 --- a/.meteor/packages +++ b/.meteor/packages @@ -40,6 +40,5 @@ gadicc:blaze-react-component # Add braze to react mixmax:smart-disconnect # Disconnect on lost focus flowkey:raven # js errors davidsichau:piwik # Stats -thebakery:ipgeocoder mdg:geolocation natestrauser:publish-performant-counts diff --git a/.meteor/versions b/.meteor/versions index c98ac3d..0dfab42 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -104,7 +104,6 @@ templating@1.3.2 templating-compiler@1.3.3 templating-runtime@1.3.2 templating-tools@1.1.2 -thebakery:ipgeocoder@0.1.2 themeteorchef:bert@2.1.3 tmeasday:check-npm-versions@0.3.1 tracker@1.1.3 diff --git a/designs/icons-tcef.svg b/designs/icons-tcef.svg new file mode 100644 index 0000000..ce904f3 --- /dev/null +++ b/designs/icons-tcef.svg @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/imports/api/ActiveFires/server/publications.js b/imports/api/ActiveFires/server/publications.js index 22a4ed8..5e9d29f 100644 --- a/imports/api/ActiveFires/server/publications.js +++ b/imports/api/ActiveFires/server/publications.js @@ -32,15 +32,25 @@ var NullOr = function (type) { }); }; +// http://wiki.openstreetmap.org/wiki/Zoom_levels +// http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Resolution_and_Scale const zoomMetersPerPixel = [156412, 78206, 39103, 19551, 9776, 4888, 2444, 1222, 610.984, 305.492, 152.746, 76.373, 38.187, 19.093, 9.547, 4.773, 2.387, 1.193, 0.596, 0.298]; -var activefires = function(zoom, lat, lng) { +// http://cwestblog.com/2012/11/12/javascript-degree-and-radian-conversion/ +Math.radians = function(degrees) { + return degrees * Math.PI / 180; +}; + +var activefires = function(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); - // console.log("Meters per pixel: " + zoomMetersPerPixel[zoom]); + var resolution = 156543.03 * Math.cos(Math.radians(lat)) / Math.pow(2,zoom); + // console.log(`Meters per pixel ${zoomMetersPerPixel[zoom]}, resolution ${resolution} meters x pixel`); + var distance = Math.trunc(Math.max(resolution*height, resolution*width)); + // 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(geoData); return ActiveFires.find({ @@ -51,16 +61,19 @@ var activefires = function(zoom, lat, lng) { coordinates: [ lng, lat] }, $minDistance: 0, - $maxDistance: zoomMetersPerPixel[zoom] * 1000 + $maxDistance: distance } } }); } -Meteor.publish('activefiresmyloc', function(zoom, lat, lng) { +Meteor.publish('activefiresmyloc', function(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 === null || lng === null) { var clientIP = this.connection.clientAddress; if (clientIP === '127.0.0.1') { @@ -71,5 +84,5 @@ Meteor.publish('activefiresmyloc', function(zoom, lat, lng) { lat = location.latitude; lng = location.longitude; } - return activefires(zoom, lat, lng); + return activefires(zoom, lat, lng, height, width); }); diff --git a/imports/startup/server/geolocation.js b/imports/startup/server/geolocation.js index 6e50226..fd74a15 100644 --- a/imports/startup/server/geolocation.js +++ b/imports/startup/server/geolocation.js @@ -1,23 +1,43 @@ import { Meteor } from 'meteor/meteor'; -// https://atmospherejs.com/thebakery/ipgeocoder -Meteor.startup(function(){ - // TODO, download from time to time in :public/GeoLite2-City.mmdb.gz - // http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz - IPGeocoder.load(Meteor.absoluteUrl() + 'GeoLite2-City.mmdb.gz'); - // load() stored it in: /tmp/GeoLite2-City.mmdb -}); +import maxmind from 'maxmind'; + +// https://stackoverflow.com/questions/13969655/how-do-you-check-whether-the-given-ip-is-internal-or-not +function isPrivateIP(ip) { + var parts = ip.split('.'); + return parts[0] === '10' || + parts[0] === '127' || + (parts[0] === '172' && (parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31)) || + (parts[0] === '192' && parts[1] === '168'); +} Meteor.methods({ geo: function() { + // https://stackoverflow.com/questions/14843232/how-to-get-the-user-ip-address-in-meteor-server/22657421#22657421 var clientIP = this.connection.clientAddress; - if (clientIP === '127.0.0.1') { + if (isPrivateIP(clientIP)) { clientIP = '80.58.61.250' // Some Spain IP address } - var geoData = IPGeocoder.geocode(clientIP); - // console.log(geoData); - return geoData; + console.log(`Geolocating ${clientIP}`); + + // https://developers.google.com/web/fundamentals/primers/promises + var promise = new Promise(function(resolve, reject) { + // do a thing, possibly async, then… + // TODO: cron download GeoLite-City + // http://dev.maxmind.com/geoip/geoip2/geolite2/ + maxmind.open(process.env.PWD + '/private/GeoLite2-City.mmdb', (err, cityLookup) => { + if (err) { + reject(console.error(`Failed to geolite ${clientIP}, ${err}`)); + } + else { + var city = cityLookup.get(clientIP); + // console.log(city); + resolve(city); + } + }); + }) + return promise.await(); } }); diff --git a/imports/ui/layouts/App/App.js b/imports/ui/layouts/App/App.js index 553e710..b37787d 100644 --- a/imports/ui/layouts/App/App.js +++ b/imports/ui/layouts/App/App.js @@ -29,7 +29,7 @@ import Terms from '../../pages/Terms/Terms'; import Privacy from '../../pages/Privacy/Privacy'; import License from '../../pages/License/License'; import ReSendEmail from '../../components/ReSendEmail/ReSendEmail'; -import Reconnect from '../../components/Reconnect/Reconnect'; +/* import Reconnect from '../../components/Reconnect/Reconnect';*/ // i18n import { I18nextProvider } from 'react-i18next'; import i18n from '/imports/startup/client/i18n'; @@ -79,7 +79,7 @@ const App = props => (