npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

contraction-hierarchy-js

v1.2.1

Published

Contraction Hierarchy

Downloads

23

Readme

contraction-hierarchy-js

:scream: Scary-Fast Pathfinding for NodeJS using Contraction Hierarchies

When to Use a Contraction Hierarchy

The typical use case of using a Contraction hierarchy over Dijkstras Algorithm or a heuristic-based approach like A* is where you have static data that changes infrequently. A good example would be a road network (one that does not attempt to account for road closures and traffic). Being able to pre-process networks up front allows for a greatly increased speed of pathfinding at runtime.

Changes in 1.0

  • Ability to work with non-geojson data via a manual edge API.
  • Ability to work with directed networks (using the manual API)
  • Export data as GeoJSON, edge _id list, node list, or edge properties array.

Install

npm install --save contraction-hierarchy-js

Quickstart

const fs = require('fs');
const { Graph, CoordinateLookup } = require('../index.js');

const geofile = fs.readFileSync('../networks/basic.geojson', 'utf8');
const geojson = JSON.parse(geofile);

const graph = new Graph(geojson);

// build hierarchy.  this step may take a while.
graph.contractGraph();

const finder = graph.createPathfinder({ ids: true, path: true, nodes: true, properties: true });

// create a coordinate lookup to be able to input arbitrary coordinate pairs
// and return the nearest coordinates in the network
const lookup = new CoordinateLookup(graph);
const coords1 = lookup.getClosestNetworkPt(-116.45, 41.96);
const coords2 = lookup.getClosestNetworkPt(-117.45, 40.96);

const path = finder.queryContractionHierarchy(coords1, coords2);

console.log(path);

API

Graph

const graph = new Graph(geojson, options);

Creates a new Graph object.

Both parameters; geojson and options are optional.

geojson is a GeoJSON linestring network; example.

  • Your features properties object must contain a unique _id (number) and a _cost (number).
  • GeoJSON networks are assumed to be undirected networks. If you need to construct a directed network, please use the manual API.

options is an object with one attribute:

  • debugMode - defaults to false. Set it to true to see miscellaneous data validation and contraction progress messages.

Graph Methods

graph.addEdge(start, end, edge_properties)

If your data is not GeoJSON, you can instead use the manual API.

  • start and end are string values corresponding to Node names. edge_properties is an object listing the properties of the edge between the start and end nodes.
  • edge_properties object must contain a unique _id (number) and a _cost (number).
graph.contractGraph()

The contractGraph method will build a contraction hierarchy from your input data. This step could take a while! For extremely large datasets (example: highly detailed road networks of large geographic areas) the build time could extend for hours, or not be feasible at all. I would highly recommend using { debugMode = true } as your options parameter when initializing your Graph, to give you a gauge on the contraction progress of your network.

const finder = graph.createPathfinder(options);

The createPathfinder method creates a pathFinder object with which you can use to query your network. The main purpose is to be able to configure graph outputs with the options object.

By default, any queries you make on the network will return with { total_cost: (number) } for your given path. To add additional properties, you can supply either/or/none of the following for the options object:

{ids: true}: Will return an ordered array of edge IDs corresponding to the _id attribute in the original geojson.

{path: true}: Will return a geojson linestring path with all original geojson attributes.

{nodes: true}: Will return an ordered array of nodes that the path follows.

{properties: true}: Will return an ordered array of properties of each edge.

Load and Save

graph.saveCH()

Create a stringified serialized version of your contracted network. This is immensely useful to be able to re-use your contracted network, without having to incur the cost of contraction repeatedly.

const graph = graph.loadCH(network)

Load a stringified serialized contracted network (that was saved previously via the saveCH method).

graph.savePbfCH(filename);

Save network as a PBF file (much more compact!) NodeJS only.

graph.savePbfCH(filename);

Save network as a PBF file (much more compact!) NodeJS only.

graph.loadPbfCH(buffer);

Load a network that was saved to PBF. Can be used in the browser or NodeJS.

Finder Methods

const path = finder.queryContractionHierarchy(start, end);

To query the graph, use the queryContractionHierarchy method. It expects start and end coordinates, where each is in the form: [-110.45, 35.4] ([lng, lat])

Coordinate Lookup

const lookup = new CoordinateLookup(graph);
const coords1 = lookup.getClosestNetworkPt(-101.359, 43.341);
const coords2 = lookup.getClosestNetworkPt(-91.669, 40.195);

When using queryContractionHierarchy, your start and end points must correspond exactly with start/end points of lines in your graph. Because this can be difficult to arrange without a lot of manual work, I've built a helper to be able to find the closest coordinates in your graph to any arbitrary coordinate you supply.

Performance

This is not benchmarking per se, as comparing a dijkstra implementation to a contraction hierarchy is not an apples to apples comparison. (Contraction hierarchies require a lengthy pre-processing step, whearas Dijkstras algorithm does not.)

Here is a comparison against a very fast implementation of Dijkstra via Ngraph Path

Dataset: USA major roads network (via freight analysis framework) Nodes: 135308 Edges: 340981

--max_old_space_size=7000 AWS t2.large (2vCPU, 8GB)

| | Contraction Time | 10,000 Random Routes | ms per route | | --------------------------- | ---------------- | -------------------- | ------------- | | * Dijkstra (via Ngraph) | 0 ms | 1232269 ms | 123.23 ms | | * Contraction Hierarchy JS | 972786 ms | 3616 ms | 0.36 ms | | ** Contraction Hierarchy JS | 972786 ms | 24013 ms | 2.40 ms |

  • Basic (only distance calculated)

** Enriched (construct GeoJSON path)

As you can see, if your data is not highly dynamic, it makes sense to contract your network to get a tremendous runtime boost in speed.

I don't quite believe it myself, TBH, but there it is.

Credits

Quite a few of the program internals were inspired from or directly ported from the excellent project NGraph. If you need a feature rich pathfinding solution and a contraction step is a dealbreaker, I highly recommend checking out NGraph.

The coordinate lookup would not have been possible without the geokdbush library. Mourner is also the original creator of TinyQueue, a derivation of which is included in this program. Including this queue brought about some unbelievable performance improvements.

Issues

Larger networks are problematic. Time to contract is obviously much higher. Memory issues start to become a factor as well. Become aquainted with the NodeJS command line argument: --max_old_space_size=. If you run into this, check out this stackoverflow post.