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

multimodal-isochrones

v1.3.0

Published

Compute lines of equal travel times (isochrones) in a multimodal transport graph

Downloads

8

Readme

multimodal-isochrones

Compute lines of equal travel times (isochrones) in a multimodal transport graph.

Isochrone maps display areas of similar travel time to a selected starting location on the map. There are several algorithms that can be used to compute the minimum travel distance on a graph, however they don't take into account the additional cost of switching traveling mode in a door-to-door approach.

Graphs are usually represented as nodes connected by edges. In a complex transport systems edges can belong to different railway or bus lines and there's usually an additional cost (monetary or travel time) in switching line at any given station (node).

multimodal-isochrones finds the shortest path between a node of the graph and any other node taking into account the travel cost and the cost of transfers.

Node

A node is simply represented by a unique String.

For complex transport networks this could be a single station, regardless of the number of lines that crosses it.

Edge

An edge is a direct connection between two nodes (undirected). It's represented by

  • from the origin node
  • to the destination node
  • line the line identifier
  • time the cost value, the algorithm will try to minimize it

Populate the graph

const isochrones = require('multimodal-isochrones');

/* Generate a graph with two nodes connected by an edge on line1
 * that takes 5 minutes to cross
 */
const graph = isochrones()
  .addEdge({ from: 'node1', to: 'node2', line: 'line1', time: 5 });

You can chain any number of addNode and addEdge, in any order to build your graph, the only constraint is that when creating an edge both nodes must be already added.

You can represent multiple lines and the time it take to transfer between them by calling

const graph = isochrones()
  // fist line, going from north to south
  .addEdge({ from: 'north', to: 'center', line: 'vertical-line', time: 5 })
  .addEdge({ from: 'south', to: 'center', line: 'vertical-line', time: 5 })
  // second line, from east to west
  .addEdge({ from: 'west', to: 'center', line: 'horizontal-line', time: 4 })
  .addEdge({ from: 'east', to: 'center', line: 'horizontal-line', time: 4 })
  // it takes some time to change line at the center
  .addConnection('center', 'vertical-line', 'horizontal-line', 2);

options

The main function isochrones accepts an optional options object with

const graph = isochrones({
  defaultConnectionTime: 4, // Time to transfer line at any node, default 0
});

Query

In order to get a list of nodes you need a query object, that can be constructed calling

const query = graph.from('node1');

const nodes = query.get();

.get() returns all the nodes that match the constraints defined by the query object. The result is an array of objects like

{
  node: 'node2', // the target node
  paths: [ // a list of paths the go from a source node to the target
    {
      from: 'node1', // the source node
      lines: ['line1'], // the list of lines traversed
      time: 5, // total time from source to target
    }
  ],
}

Max travel time

Limit the results by the total travel time calling

const query = graph.from('node1');

const nodes = query.maxTime(30).get();

This will return all nodes reachable in 30 minutes (assuming you count time in minutes) from node1.

Max number of line transfers

Limit the results by the total number of lines that can be crossed

const query = graph.from('node1');

const nodes = query.maxLines(2).get();

This will return all nodes reachable with a maximum of two lines, so the ones on the same line as node1 and the ones that are directly connected to them.

Intersection

A simple query (using .from) returns all nodes reachable from a starting node, for more advanced results you can intersect any arbitrary number of queries.

const intersection = graph.intersect(
  graph.from('node1').maxTime(10).maxLines(1),
  graph.from('node2').maxTime(30).maxLines(2),
  // any number of them
);

const nodes = intersection.get();

The example query above will return all nodes that are reachable from both node1 and node2 with the extra constraints of being within 10 minutes and one line from node1 and 30 minutes and max two lines from node2.

Join

If you have equivalent starting nodes and you want to get the shortest paths from either of them, you can join two queries

const join = graph.join(
  graph.from('node1').maxTime(10),
  graph.from('node2').maxTime(10),
);

const nodes = join.get();

The example will return all nodes that are reachable from eiter node1 or node2 with the desired constraints. Only the shortest path will be included in the results.

join and intersect can be combined

graph.join(
  graph.intersect(
    graph.from('node1').maxTime(10),
    graph.from('node2').maxTime(10),
  ),
  graph.from('node3').maxTime(10),
).get();

// or

graph.intersect(
  graph.join(
    graph.from('node1').maxTime(10),
    graph.from('node2').maxTime(10),
  ),
  graph.from('node3').maxTime(10),
).get();

Utility methods

const isochrones = require('multimodal-isochrones');
const graph = isochrones(); // .addEdge

const lines = isochrones.getTraversedLines(
  graph.from('node1').get()
);
/*
lines contains all line ids included in the result list
 */

Additional data on the paths object

It's possible to assign any additional data to the result paths calling pathData on the query object.

const node = graph.join(
  graph.from('node1').pathData({ cost: 100 }),
  graph.from('node2').pathData({ cost: 200 }),
);
/*
nodes = [
  { node: 'node3', paths: [{ from: 'node1', cost: 100, ... }]},
  ...
]
 */