Giter VIP home page Giter VIP logo

Comments (5)

cirlam avatar cirlam commented on August 27, 2024 1

After some tweaks and modifications, this passes the same tests as used on your other geographical libraries - full repository here: https://github.com/cirlam/georbush

from rbush.

cirlam avatar cirlam commented on August 27, 2024 1

No problem! Glad you find it useful. I can't take credit for it though, the hard work done by @mourner on geokdbush and geoflatbush made it possible.

from rbush.

mourner avatar mourner commented on August 27, 2024

I'd like to do that but don't have a timeline for that. The two approaches (geoflatbush vs geokdbush) are roughly equivalent — I like the flatbush one slightly more because it seems simpler.

from rbush.

cirlam avatar cirlam commented on August 27, 2024

Thanks for the advice! I've quickly combined your rbush-knn with the geoflatbush library to give me the code below. I think it all seems sensible, but I've yet to test it - any criticism is welcome.

var Queue = require('tinyqueue');

const earthRadius = 6371;
const earthCircumference = 40007;
const rad = Math.PI / 180;

exports.around = function(tree, lng, lat,n = Infinity, maxDistance = Infinity, predicate) {
    var node = tree.data,
    result = [],
    toBBox = tree.toBBox,
    i, child, dist, candidate;
    
    var queue = new Queue([],compareDist);

    const cosLat = Math.cos(lat * rad); 
    const sinLat = Math.sin(lat * rad);

    while (node) {
        for (i = 0; i < node.children.length; i++) {
            child = node.children[i];
            childBBox = node.leaf ? toBBox(child) : child;

            const minLng = childBBox.minX;
            const minLat = childBBox.minY; 
            const maxLng = childBBox.maxX; 
            const maxLat = childBBox.maxY; 

            dist = boxDist(lng, lat, minLng, minLat, maxLng, maxLat, cosLat, sinLat);

            if (!maxDistance || dist <= maxDistance) {
                queue.push({
                    node: child,
                    isItem: node.leaf,
                    dist: dist
                });
            }
        }

        while (queue.length && queue.peek().isItem) {
            candidate = queue.pop().node;
            if (!predicate || predicate(candidate))
                result.push(candidate);
            if (n && result.length === n) return result;
        }

        node = queue.pop();
        if (node) node = node.node;
    }

    return result;
}


function compareDist(a, b) {
    return a.dist - b.dist;
}

// lower bound for distance from a location to points inside a bounding box
function boxDist(lng, lat, minLng, minLat, maxLng, maxLat, cosLat, sinLat) {
    if (minLng === maxLng && minLat === maxLat) {
        return greatCircleDist(lng, lat, minLng, minLat, cosLat, sinLat);
    }

    // query point is between minimum and maximum longitudes
    if (lng >= minLng && lng <= maxLng) {
        if (lat <= minLat) return earthCircumference * (minLat - lat) / 360; // south
        if (lat >= maxLat) return earthCircumference * (lat - maxLat) / 360; // north
        return 0; // inside the bbox
    }

    // query point is west or east of the bounding box;
    // calculate the extremum for great circle distance from query point to the closest longitude
    const closestLng = (minLng - lng + 360) % 360 <= (lng - maxLng + 360) % 360 ? minLng : maxLng;
    const cosLngDelta = Math.cos((closestLng - lng) * rad);
    const extremumLat = Math.atan(sinLat / (cosLat * cosLngDelta)) / rad;

    // calculate distances to lower and higher bbox corners and extremum (if it's within this range);
    // one of the three distances will be the lower bound of great circle distance to bbox
    let d = Math.max(
        greatCircleDistPart(minLat, cosLat, sinLat, cosLngDelta),
        greatCircleDistPart(maxLat, cosLat, sinLat, cosLngDelta));

    if (extremumLat > minLat && extremumLat < maxLat) {
        d = Math.max(d, greatCircleDistPart(extremumLat, cosLat, sinLat, cosLngDelta));
    }

    return earthRadius * Math.acos(d);
}

// distance using spherical law of cosines; should be precise enough for our needs
function greatCircleDist(lng, lat, lng2, lat2, cosLat, sinLat) {
    const cosLngDelta = Math.cos((lng2 - lng) * rad);
    return earthRadius * Math.acos(greatCircleDistPart(lat2, cosLat, sinLat, cosLngDelta));
}

// partial greatCircleDist to reduce trigonometric calculations
function greatCircleDistPart(lat, cosLat, sinLat, cosLngDelta) {
    const d = sinLat * Math.sin(lat * rad) + cosLat * Math.cos(lat * rad) * cosLngDelta;
    return Math.min(d, 1);
}

exports.distance = function(lng, lat, lng2, lat2) {
    return greatCircleDist(lng, lat, lng2, lat2, Math.cos(lat * rad), Math.sin(lat * rad));
}

from rbush.

volkanunsal avatar volkanunsal commented on August 27, 2024

@cirlam Thanks for implementing georbush. It works great!

from rbush.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.