Skip to content

Instantly share code, notes, and snippets.

@tomfun
Last active February 22, 2016 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomfun/431434bc026b143a65d3 to your computer and use it in GitHub Desktop.
Save tomfun/431434bc026b143a65d3 to your computer and use it in GitHub Desktop.
rough location filter with same minimum/maximum lattitude/longitude range
"use strict";
const EARTH_RADIUS = 6371;
function degToRad(deg) {
return deg * Math.PI / 180;
}
/**
* @param {Number} distance km
* @returns {Number}
*/
function approximateLatitude(distance) {
return 180 * distance / EARTH_RADIUS / Math.PI;
}
/**
* Return min/max lat/lon for approximately search by coordinates
*
* @param {Number} distance MUST be fixed for caching, km
* @param {Number} latitude search point center
* @param {Number} longitude search point center
* @returns {{latMin: number, latMax: number, lonMin: number, lonMax: number, latIgnore: boolean, lonIgnore: boolean}}
*/
function approximateDistance(distance, latitude, longitude) {
const lat = parseFloat(latitude);
const lon = parseFloat(longitude);
const deltaLat = approximateLatitude(distance);//сдвиг по широте
let tmpLon = Math.abs(lat) + deltaLat;
const resMinLat = Math.floor((lat - deltaLat) / deltaLat) * deltaLat;
const resMaxLat = Math.ceil((lat + deltaLat) / deltaLat) * deltaLat;
tmpLon = Math.ceil(tmpLon / deltaLat) * deltaLat;//ближе к экватору
if (tmpLon > 90) {
tmpLon = 90;
}
const r = EARTH_RADIUS * Math.cos(degToRad(tmpLon));
const deltaLon = 360 * distance / (Math.PI * r * 2);
const resMaxLon = Math.ceil((lon + deltaLon) / deltaLon) * deltaLon;
const resMinLon = Math.floor((lon - deltaLon) / deltaLon) * deltaLon;
return {
//latMin: resMinLat < -90 ? -90 : resMinLat,
//latMax: resMaxLat > 90 ? 90 : resMaxLat,
//lonMin: resMinLon < -180 ? -180 : resMinLon,
//lonMax: resMaxLon > 180 ? 180 : resMaxLon
latMin: resMinLat,
latMax: resMaxLat,
lonMin: resMinLon,
lonMax: resMaxLon,
latIgnore: deltaLat >= 90,
lonIgnore: deltaLon >= 180
};
}
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2 - lat1); // deg2rad below
var dLon = deg2rad(lon2 - lon1);
var a =
Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI / 180)
}
//console.log(180 * 100 * 360 * 100 * 3)
var _ = require('lodash');
var testResult = {};
_.each(_.range(1, 11), function (v) {
testResult[v] = {
latMin: 90,
latMax: -90,
lonMin: 180,
lonMax: -180
};
});
function updateError(key, lat1, lon1) {
var tr = testResult[key];
if (!tr) {
console.error(key);
process.exit(1);
}
tr.latMin = Math.min(tr.latMin, lat1);
tr.latMax = Math.max(tr.latMax, lat1);
tr.lonMax = Math.max(tr.lonMax, lon1);
tr.lonMin = Math.min(tr.lonMin, lon1);
}
var maxDistance = 0;
var latStart = 90, latStep = 0.5, latHole = 0;
var lonStart = 180, lonStep = 0.5;
var distStart = 5, distStep = 5, distEnd = 10;
var len = ((latStart - latHole) * 2 / latStep) * (lonStart * 2 / lonStep) * ((distEnd - distStart) / distStep);
//var len = (latStart * 2 / latStep) * (lonStart * 2 / lonStep) * ((distEnd - distStart)/ distStep);
var passedOver = 0;
var flag1 = false;
var flag2 = false;
_.each(_.union(_.range(-latStart, -latHole, latStep), _.range(latHole, latStart + latStep, latStep)), function (lat) {
//_.each(_.range(-latStart, latStart + latStep, latStep), function (lat) {
_.each(_.range(-lonStart, lonStart + lonStep, lonStep), function (lon) {
_.each(_.range(distStart, distEnd, distStep), function (distance) {
var distanceTest = distance;
var cb = function () {
var res = approximateDistance(distance, lat, lon);
if (res.latIgnore) {
//console.log('aaalat', distance);
flag1 = true;
return;
}
if (res.lonIgnore) {
//console.log('aaalon', distance);
flag2 = true;
return;
}
updateError(9, res.latMin, res.lonMin);
updateError(10, res.latMax, res.lonMax);
//console.log(lat, lon, distance)
//console.log(res)
var dist = getDistanceFromLatLonInKm(lat, lon, res.latMax, res.lonMax);
//console.log(dist)
maxDistance = Math.max(maxDistance, dist);
if (dist < distanceTest) {
console.error("**t1**", Math.round(dist), lat, lon);
updateError(1, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, res.latMax, res.lonMin);
maxDistance = Math.max(maxDistance, dist);
//console.log(dist)
if (dist < distanceTest) {
console.error("**t2**", Math.round(dist), lat, lon);
updateError(2, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, res.latMin, res.lonMax);
maxDistance = Math.max(maxDistance, dist);
//console.log(dist)
if (dist < distanceTest) {
console.error("**t3**", Math.round(dist), lat, lon);
updateError(3, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, res.latMin, res.lonMin);
maxDistance = Math.max(maxDistance, dist);
//console.log(dist)
if (dist < distanceTest) {
console.error("**t4**", Math.round(dist), lat, lon);
updateError(4, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, 90, 180);
if (dist < distanceTest) {
console.log("**t5**", Math.round(dist), lat, lon);
updateError(5, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, 90, -180);
if (dist < distanceTest) {
console.log("**t6**", Math.round(dist), lat, lon);
updateError(6, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, -90, 180);
if (dist < distanceTest) {
console.log("**t7**", Math.round(dist), lat, lon);
updateError(7, lat, lon);
}
dist = getDistanceFromLatLonInKm(lat, lon, -90, -180);
if (dist < distanceTest) {
console.log("**t8**", Math.round(dist), lat, lon);
updateError(8, lat, lon);
}
};
cb();
if (passedOver++ % 1000000 === 0) {
console.log(100 * passedOver / len, "% (", passedOver, len, ")");
console.log(testResult);
}
//process.nextTick(cb);
});
});
});
console.log("\n DONE", testResult, flag1, flag2, maxDistance);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment