├── .dockerignore
├── .vscode
├── settings.json
└── launch.json
├── apps
├── frontend
│ ├── src
│ │ ├── react-app-env.d.ts
│ │ ├── index.tsx
│ │ ├── app.tsx
│ │ ├── utilities
│ │ │ ├── interfaces
│ │ │ │ ├── graph.ts
│ │ │ │ ├── misc.ts
│ │ │ │ └── map.ts
│ │ │ ├── use-state-ref.tsx
│ │ │ └── functions.ts
│ │ ├── components
│ │ │ ├── algorithm-select
│ │ │ │ └── index.tsx
│ │ │ ├── color-picker
│ │ │ │ └── index.tsx
│ │ │ ├── region-select
│ │ │ │ └── index.tsx
│ │ │ └── map
│ │ │ │ └── index.tsx
│ │ ├── app.css
│ │ ├── about.tsx
│ │ └── main.tsx
│ ├── public
│ │ ├── robots.txt
│ │ ├── favicon.ico
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── index.html
│ ├── config-overrides.js
│ ├── tsconfig.json
│ ├── package.json
│ └── README.md
├── backend
│ ├── src
│ │ ├── algorithms
│ │ │ ├── README.md
│ │ │ ├── landmark-scan.ts
│ │ │ ├── scan-select-landmarks.ts
│ │ │ ├── bfs.ts
│ │ │ ├── dijkstra.ts
│ │ │ ├── a-star.ts
│ │ │ ├── bidirectional-dijkstra.ts
│ │ │ ├── bidirectional-a-star-alt.ts
│ │ │ ├── bidirectional-a-star.ts
│ │ │ └── algorithm-handler.ts
│ │ ├── utils
│ │ │ ├── catch-async.ts
│ │ │ ├── response-handler.ts
│ │ │ ├── interfaces
│ │ │ │ ├── misc.ts
│ │ │ │ └── graph.ts
│ │ │ └── functions.ts
│ │ ├── errors
│ │ │ └── custom-error.ts
│ │ ├── database
│ │ │ └── landmarks.ts
│ │ ├── data
│ │ │ ├── regions.ts
│ │ │ └── algorithms.ts
│ │ ├── data-structures
│ │ │ ├── lat-lon-index-tree.ts
│ │ │ ├── adjacency-list.ts
│ │ │ ├── landmark-list.ts
│ │ │ └── contraction-hierarchy.ts
│ │ ├── app.ts
│ │ ├── server.ts
│ │ ├── routes
│ │ │ └── index.ts
│ │ └── controllers
│ │ │ └── main.ts
│ ├── landmarks-note.md
│ ├── package.json
│ └── tsconfig.json
└── preprocess
│ ├── generate-adj-list.sh
│ ├── package.json
│ ├── get-map-boundary.js
│ ├── create-adj-list-from-osm.js
│ └── package-lock.json
├── docs
└── screenshot.png
├── .gitignore
├── Dockerfile
├── README.md
└── osm_parser.py
/.dockerignore:
--------------------------------------------------------------------------------
1 | **/node_modules
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
--------------------------------------------------------------------------------
/apps/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/docs/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bihan001/world-graph/HEAD/docs/screenshot.png
--------------------------------------------------------------------------------
/apps/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/apps/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bihan001/world-graph/HEAD/apps/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/apps/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bihan001/world-graph/HEAD/apps/frontend/public/logo192.png
--------------------------------------------------------------------------------
/apps/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Bihan001/world-graph/HEAD/apps/frontend/public/logo512.png
--------------------------------------------------------------------------------
/apps/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import App from './app';
3 | import 'antd/dist/antd.css';
4 |
5 | ReactDOM.render( , document.getElementById('root'));
6 |
--------------------------------------------------------------------------------
/apps/frontend/src/app.tsx:
--------------------------------------------------------------------------------
1 | import Main from './main';
2 |
3 | const App = () => {
4 | return (
5 | <>
6 |
7 | >
8 | );
9 | };
10 |
11 | export default App;
12 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/README.md:
--------------------------------------------------------------------------------
1 | Latitudes and Longitudes: Degrees
2 |
3 | Heuristic Calculation(Current): meters
4 |
5 | Graph weights(Current): meters
6 |
7 | Everything in meters, like everything should be.
8 |
--------------------------------------------------------------------------------
/apps/backend/src/utils/catch-async.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response, NextFunction } from 'express';
2 |
3 | export default (fn: Function) => {
4 | return (req: Request, res: Response, next: NextFunction) => {
5 | fn(req, res, next).catch((err: Error) => next(err));
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/apps/preprocess/generate-adj-list.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Run ./generate-adj-list input_file.osm output_file_prefix
4 |
5 | osmfilter $1 --keep="highway=*" >tmp.xml
6 | node get-map-boundary.js tmp.xml >$2.boundaries.txt
7 | node create-adj-list-from-osm.js tmp.xml >$2.json
8 | rm -f tmp.xml
9 |
--------------------------------------------------------------------------------
/apps/preprocess/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preprocess",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Bihan Chakraborty",
10 | "license": "ISC",
11 | "dependencies": {
12 | "osm-read": "^0.7.0"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/apps/backend/src/errors/custom-error.ts:
--------------------------------------------------------------------------------
1 | export default class CustomError extends Error {
2 | statusCode: number;
3 | status: string;
4 | constructor(message: string, statusCode: number) {
5 | super(message);
6 | this.statusCode = statusCode;
7 | this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
8 | Error.captureStackTrace(this, this.constructor);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .turbo
2 | node_modules
3 | logs
4 | .pnp
5 | .pnp.js
6 |
7 | # Environment variables
8 | env.*
9 | *.env
10 | .env.local
11 | .env.development.local
12 | .env.test.local
13 | .env.production.local
14 |
15 | # testing
16 | coverage
17 | *.log
18 |
19 | # production
20 | build
21 | dist
22 |
23 | # misc
24 | .DS_Store
25 | *.xml
26 | *.osm
27 | *.boundaries.txt
28 |
29 | npm-debug.log*
30 | yarn-debug.log*
31 | yarn-error.log*
32 |
--------------------------------------------------------------------------------
/apps/frontend/src/utilities/interfaces/graph.ts:
--------------------------------------------------------------------------------
1 | export interface Vertex {
2 | lat: number;
3 | lon: number;
4 | adj: Array;
5 | w: Array;
6 | }
7 |
8 | export interface Edge {
9 | src: Array;
10 | dest: Array;
11 | weight: number;
12 | }
13 |
14 | export interface AdjacencyList {
15 | [index: string]: Vertex;
16 | }
17 |
18 | export interface LatLon {
19 | lat: number;
20 | lon: number;
21 | }
22 |
--------------------------------------------------------------------------------
/apps/backend/src/utils/response-handler.ts:
--------------------------------------------------------------------------------
1 | import CustomError from '../errors/custom-error';
2 |
3 | export const SuccessResponse = (data: any, message?: string) => ({
4 | success: true,
5 | status: 'success',
6 | message: message || 'Success',
7 | data,
8 | });
9 |
10 | export const ErrorResponse = (err: Error) => ({
11 | success: false,
12 | status: err instanceof CustomError && `${err.statusCode}`.startsWith('4') ? 'fail' : 'error',
13 | message: err.message,
14 | error: err,
15 | });
16 |
--------------------------------------------------------------------------------
/apps/frontend/src/utilities/use-state-ref.tsx:
--------------------------------------------------------------------------------
1 | import { useState, useRef, useEffect, MutableRefObject, Dispatch } from 'react';
2 |
3 | function useStateRef(
4 | initialValue: T
5 | ): [value: T, setValue: Dispatch>, ref: MutableRefObject] {
6 | const [value, setValue] = useState(initialValue);
7 |
8 | const ref = useRef(value);
9 |
10 | useEffect(() => {
11 | ref.current = value;
12 | }, [value]);
13 |
14 | return [value, setValue, ref];
15 | }
16 |
17 | export default useStateRef;
18 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "ts-node-debugger",
6 | "type": "node",
7 | "request": "launch",
8 | "runtimeExecutable": "node",
9 | "runtimeArgs": ["--nolazy", "-r", "ts-node/register/transpile-only"],
10 |
11 | "args": ["src/server.ts"],
12 |
13 | "cwd": "${workspaceRoot}/backend",
14 | "internalConsoleOptions": "openOnSessionStart",
15 | "skipFiles": ["/**", "node_modules/**"]
16 | }
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/apps/frontend/config-overrides.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const fs = require('fs');
3 | const rewireBabelLoader = require('react-app-rewire-babel-loader');
4 |
5 | const appDirectory = fs.realpathSync(process.cwd());
6 | const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
7 |
8 | module.exports = function override(config, env) {
9 | config = rewireBabelLoader.include(config, resolveApp('../../node_modules/@react-leaflet'));
10 | config = rewireBabelLoader.include(config, resolveApp('../../node_modules/react-leaflet'));
11 |
12 | return config;
13 | };
14 |
--------------------------------------------------------------------------------
/apps/frontend/src/utilities/interfaces/misc.ts:
--------------------------------------------------------------------------------
1 | export interface Region {
2 | key: string;
3 | displayName: string;
4 | description: string;
5 | size: string;
6 | dataUrl: string;
7 | boundary: [number, number, number, number];
8 | }
9 |
10 | export interface Algorithm {
11 | key: string;
12 | displayName: string;
13 | description: string;
14 | requirements: string;
15 | timeComplexity: string;
16 | spaceComplexity: string;
17 | }
18 |
19 | export interface RegionsData {
20 | [key: string]: Region;
21 | }
22 |
23 | export interface AlgorithmsData {
24 | [key: string]: Algorithm;
25 | }
26 |
--------------------------------------------------------------------------------
/apps/backend/src/database/landmarks.ts:
--------------------------------------------------------------------------------
1 | import mongoose from 'mongoose';
2 | import { LandMarks } from '../utils/interfaces/graph';
3 |
4 | const LandMarkModel = mongoose.model('Landmark', new mongoose.Schema({}), 'landmarks');
5 |
6 | export const uploadLandmarks = async (landmarks: LandMarks) => {
7 | try {
8 | console.log(Object.keys(landmarks).length);
9 | const newLandmarks = new LandMarkModel({ landmarks });
10 | await newLandmarks.save();
11 | console.log('Saved all landmarks');
12 | } catch (err) {
13 | console.log(err);
14 | }
15 | };
16 |
17 | export const fetchLandmarks = () => {};
18 |
--------------------------------------------------------------------------------
/apps/frontend/src/utilities/interfaces/map.ts:
--------------------------------------------------------------------------------
1 | import { MutableRefObject } from 'react';
2 | import { AdjacencyList, Edge, LatLon, Vertex } from './graph';
3 | import { Region } from './misc';
4 |
5 | export interface Viewport {
6 | lat: number;
7 | lon: number;
8 | zoom: number;
9 | }
10 |
11 | export interface Map {
12 | edgeList: Array;
13 | allEdgeList: Array;
14 | viewport: Viewport;
15 | currentRegion: Region;
16 | traceEdgeColor: string;
17 | mainEdgeColor: string;
18 | targetMarkers: Array;
19 | setTargetMarkers: React.Dispatch>>;
20 | }
21 |
--------------------------------------------------------------------------------
/apps/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/apps/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es6",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/apps/backend/src/utils/interfaces/misc.ts:
--------------------------------------------------------------------------------
1 | export interface Region {
2 | key: string;
3 | displayName: string;
4 | description: string;
5 | size: string;
6 | dataUrl: string;
7 | boundary: [number, number, number, number];
8 | contractionAvailable: boolean;
9 | }
10 |
11 | export interface Algorithm {
12 | key: string;
13 | displayName: string;
14 | description: string;
15 | requirements: string;
16 | timeComplexity: string;
17 | spaceComplexity: string;
18 | }
19 |
20 | export interface RegionsData {
21 | [key: string]: Region;
22 | }
23 |
24 | export interface AlgorithmsData {
25 | [key: string]: Algorithm;
26 | }
27 |
--------------------------------------------------------------------------------
/apps/backend/landmarks-note.md:
--------------------------------------------------------------------------------
1 | ### Landmarks coordinates for future reference:
2 |
3 | #### Time Square:
4 |
5 | - ID: 316248245, Count: 20 (Good)
6 | - ID: 6441794709, Count: 20 (Average) (For some cases, Bidirectional A-star is better)
7 | - ID: 5017195113, Count: 15 (Better with deleted landmarks from map middle sections)
8 | - ID: 276308031, Count: 23 (670ms avg after deleting some landmarks, image below)
9 | - - C:\Users\bhkai\Pictures\Screenshots\map_670ms.png
10 |
11 | - ID: 3262708698, Count: 23 (No deletion, 250ms avg for long distances for some cases, 3.5s for mid-long distances, score: Great)
12 |
13 | - ID: 223478, Count: 23 (New map with sorted IDs)
14 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:14.20 AS production
2 |
3 | WORKDIR /world-graph
4 |
5 | RUN npm install -g npm@6
6 | RUN npm install -g typescript
7 |
8 | COPY ./apps/backend/package.json ./apps/backend/package.json
9 | COPY ./apps/frontend/package.json ./apps/frontend/package.json
10 |
11 | RUN npm install --prefix ./apps/backend
12 | RUN npm install --prefix ./apps/frontend
13 |
14 | COPY ./apps ./apps
15 |
16 | ENV NODE_ENV=production
17 |
18 | RUN npm run build --prefix ./apps/frontend
19 | RUN npm run build --prefix ./apps/backend
20 |
21 | RUN cp -r ./apps/frontend/build ./apps/backend/build
22 |
23 | WORKDIR /world-graph/apps/backend
24 |
25 | CMD [ "npm", "start" ]
26 |
--------------------------------------------------------------------------------
/apps/preprocess/get-map-boundary.js:
--------------------------------------------------------------------------------
1 | const osmread = require('osm-read');
2 |
3 | const args = process.argv.slice(2);
4 | if (args.length < 1) throw new Error('Enter file path');
5 |
6 | osmread.parse({
7 | filePath: args[0],
8 | bounds: function (bounds) {
9 | // https://wiki.openstreetmap.org/wiki/Bounding_Box
10 | // bbox = left,bottom,right,top
11 | // bbox = min Longitude , min Latitude , max Longitude , max Latitude
12 | const arr = [bounds.minlon, bounds.minlat, bounds.maxlon, bounds.maxlat];
13 | console.log('Bounds: ' + JSON.stringify(arr));
14 | },
15 | error: function (msg) {
16 | console.log('error: ' + msg);
17 | },
18 | });
19 |
--------------------------------------------------------------------------------
/apps/frontend/src/utilities/functions.ts:
--------------------------------------------------------------------------------
1 | export const getObjSize = (obj: Object) => {
2 | let size = 0;
3 | for (let key in obj) {
4 | if (obj.hasOwnProperty(key)) size++;
5 | }
6 | return size;
7 | };
8 |
9 | export const calcCrow = (lat1: number, lon1: number, lat2: number, lon2: number): number => {
10 | let R = 6371; // Earth's radius in km
11 | let dLat = toRad(lat2 - lat1);
12 | let dLon = toRad(lon2 - lon1);
13 | lat1 = toRad(lat1);
14 | lat2 = toRad(lat2);
15 |
16 | let a =
17 | Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
18 | let c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
19 | let d = R * c;
20 | return d;
21 | };
22 |
23 | // Converts numeric degrees to radians
24 | const toRad = (value: number): number => {
25 | return (value * Math.PI) / 180;
26 | };
27 |
--------------------------------------------------------------------------------
/apps/backend/src/data/regions.ts:
--------------------------------------------------------------------------------
1 | import { RegionsData } from '../utils/interfaces/misc';
2 |
3 | enum regionSizes {
4 | small = 'small',
5 | medium = 'medium',
6 | big = 'big',
7 | large = 'large',
8 | }
9 |
10 | export const regionsData: RegionsData = {
11 | timeSquareSmall: {
12 | key: 'timeSquareSmall',
13 | displayName: 'Time Square (Small)',
14 | description: 'lorem ipsum',
15 | size: regionSizes.small,
16 | dataUrl: 'timesquare_small.json',
17 | boundary: [-113.5794, 53.4978, -113.4338, 53.5746],
18 | contractionAvailable: true,
19 | },
20 | // kolkataSmall: {
21 | // key: 'kolkataSmall',
22 | // displayName: 'Kolkata (Small)',
23 | // description: 'lorem ipsum',
24 | // size: regionSizes.small,
25 | // dataUrl: 'kolkata_small.json',
26 | // boundary: [88.0746, 22.1136, 88.6954, 23.2767],
27 | // contractionAvailable: false,
28 | // },
29 | };
30 |
--------------------------------------------------------------------------------
/apps/backend/src/utils/functions.ts:
--------------------------------------------------------------------------------
1 | import { LatLon } from './interfaces/graph';
2 |
3 | const R = 6371;
4 | const p = Math.PI / 180;
5 | const sqrt = Math.sqrt;
6 | const cos = Math.cos;
7 |
8 | export const heuristic = (v: LatLon, t: LatLon): number => {
9 | // Diagonal distance
10 | // const ans = Math.max(Math.abs(v.lat - t.lat), Math.abs(v.lon - t.lon));
11 |
12 | // Manhattan distance
13 | // const ans = Math.abs(v.lat - t.lat) + Math.abs(v.lon - t.lon) * 1000;
14 |
15 | // Euclidian distance
16 | // const ans = Math.sqrt((v.lat - t.lat) ** 2 + (v.lon - t.lon) ** 2) * 1000;
17 |
18 | // ANS SHOULD BE ACTUALLY THE DISTANCE / SPEED. AND ALL EDGE WEIGHTS SHOULD BE TIME TO MOVE INSTEAD OF DISTANCE
19 | const lat1 = v.lat * p;
20 | const lon1 = v.lon * p;
21 | const lat2 = t.lat * p;
22 | const lon2 = t.lon * p;
23 | const x = (lat1 + lat2) / 2;
24 | const ans = R * sqrt((lat2 - lat1) ** 2 + cos(x) * cos(x) * (lon2 - lon1) ** 2) * 1000;
25 | return ans;
26 | };
27 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/algorithm-select/index.tsx:
--------------------------------------------------------------------------------
1 | import { Select } from 'antd';
2 | import { AlgorithmsData, Algorithm } from '../../utilities/interfaces/misc';
3 |
4 | const { Option } = Select;
5 |
6 | interface AlgorithmSelectProps {
7 | algorithms: AlgorithmsData;
8 | changeAlgorithm: (algorithm: Algorithm) => void;
9 | }
10 |
11 | const AlgorithmSelect = ({ algorithms, changeAlgorithm }: AlgorithmSelectProps) => {
12 | const handleChange = (value: string) => {
13 | changeAlgorithm(algorithms[value]);
14 | };
15 |
16 | return (
17 |
18 |
19 | Select an algorithm
20 |
21 | {Object.keys(algorithms).map((key) => (
22 |
23 | {algorithms[key].displayName}
24 |
25 | ))}
26 |
27 | );
28 | };
29 |
30 | export default AlgorithmSelect;
31 |
--------------------------------------------------------------------------------
/apps/backend/src/data-structures/lat-lon-index-tree.ts:
--------------------------------------------------------------------------------
1 | import { AdjacencyList, LatLonIndexTree, Vertex } from '../utils/interfaces/graph';
2 |
3 | export const latLonIndexTree: LatLonIndexTree = { latitudes: {}, longitudes: {} };
4 |
5 | export const createLatLonIndexTree = () => {
6 | const fullAdjacencyList = global.fullAdjacencyList;
7 | Object.keys(fullAdjacencyList).map((regionKey) => {
8 | const region: AdjacencyList = fullAdjacencyList[regionKey];
9 | Object.keys(region).map((key) => {
10 | const vertex: Vertex = region[key];
11 |
12 | const targetLat = vertex.lat.toFixed(3);
13 | const targetLon = vertex.lon.toFixed(3);
14 |
15 | if (latLonIndexTree.latitudes[targetLat]) latLonIndexTree.latitudes[targetLat].push(key);
16 | else latLonIndexTree.latitudes[targetLat] = [key];
17 |
18 | if (latLonIndexTree.longitudes[targetLon]) latLonIndexTree.longitudes[targetLon].push(key);
19 | else latLonIndexTree.longitudes[targetLon] = [key];
20 | });
21 | });
22 | };
23 |
--------------------------------------------------------------------------------
/apps/backend/src/utils/interfaces/graph.ts:
--------------------------------------------------------------------------------
1 | export interface Vertex {
2 | lat: number;
3 | lon: number;
4 | adj: Array;
5 | w: Array;
6 | }
7 |
8 | export interface Edge {
9 | src: Array;
10 | dest: Array;
11 | weight: number;
12 | }
13 |
14 | export interface AdjacencyList {
15 | [index: string]: Vertex;
16 | }
17 |
18 | export interface FullAdjacencyList {
19 | [key: string]: AdjacencyList;
20 | }
21 |
22 | export interface LatLon {
23 | lat: number;
24 | lon: number;
25 | }
26 |
27 | export interface LatIndexTree {
28 | [key: string]: Array;
29 | }
30 |
31 | export interface LonIndexTree {
32 | [key: string]: Array;
33 | }
34 |
35 | export interface LatLonIndexTree {
36 | latitudes: LatIndexTree;
37 | longitudes: LonIndexTree;
38 | }
39 |
40 | export interface LandMark {
41 | lat: number;
42 | lon: number;
43 | distances: { [key: string]: number };
44 | }
45 |
46 | export interface LandMarks {
47 | [region: string]: { [key: string]: LandMark };
48 | }
49 |
--------------------------------------------------------------------------------
/apps/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "world-graph-backend",
3 | "version": "1.0.0",
4 | "description": "Backend of the World Graph application",
5 | "main": "src/server.ts",
6 | "scripts": {
7 | "start": "NODE_ENV=production node dist/server.js",
8 | "dev": "nodemon src/server.ts",
9 | "build": "tsc -p .",
10 | "client": "npm start --prefix frontend",
11 | "dev-full": "concurrently \"npm run dev\" \"npm run client\""
12 | },
13 | "author": "Bihan Chakraborty",
14 | "license": "ISC",
15 | "dependencies": {
16 | "axios": "^1.7.9",
17 | "collections": "^5.1.12",
18 | "cors": "^2.8.5",
19 | "dotenv": "^8.2.0",
20 | "express": "^4.17.1",
21 | "mongoose": "^5.12.7",
22 | "morgan": "^1.10.0"
23 | },
24 | "devDependencies": {
25 | "@types/collections": "^5.1.1",
26 | "@types/cors": "^2.8.10",
27 | "@types/express": "^4.17.11",
28 | "@types/morgan": "^1.9.2",
29 | "concurrently": "^6.0.2",
30 | "ts-node": "^10.8.1",
31 | "typescript": "^4.2.4",
32 | "nodemon": "^2.0.19"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/apps/backend/src/data-structures/adjacency-list.ts:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 | import fs from 'fs';
3 | import { AdjacencyList, FullAdjacencyList } from '../utils/interfaces/graph';
4 | import { regionsData } from '../data/regions';
5 |
6 | global.fullAdjacencyList = {};
7 | global.revFullAdjacencyList = {};
8 |
9 | export const fetchAllRegionsFromDB = async () => {
10 | try {
11 | await Promise.all(
12 | Object.keys(regionsData).map(async (key) => {
13 | const region = regionsData[key];
14 | // const res = await axios.get(region.dataUrl);
15 | // const adjList: AdjacencyList = res.data;
16 | const adjList: AdjacencyList = JSON.parse(fs.readFileSync(region.dataUrl, 'utf-8'));
17 | console.log('Vertices:', Object.keys(adjList).length);
18 | global.fullAdjacencyList[key] = adjList;
19 | global.revFullAdjacencyList[key] = adjList;
20 | })
21 | );
22 |
23 | console.log(Object.keys(global.fullAdjacencyList).length);
24 | console.log(Object.keys(global.revFullAdjacencyList).length);
25 | } catch (err: any) {
26 | console.error(err.message);
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/color-picker/index.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { Button } from 'antd';
3 | import { SketchPicker } from 'react-color';
4 | import rgbHex from 'rgb-hex';
5 |
6 | interface ColorPickerProps {
7 | setColor: any;
8 | color: string;
9 | text: string;
10 | }
11 |
12 | const ColorPicker: React.FC = (props) => {
13 | const { color, setColor, text } = props;
14 | const [isVisible, setIsVisible] = useState(false);
15 |
16 | const handleClick = (e: any) => {
17 | setIsVisible((v) => !v);
18 | };
19 |
20 | return (
21 |
22 |
handleClick(e)}>
23 |
24 | {text || 'Pick Color'}
25 |
26 | {isVisible && (
27 |
28 | setColor('#' + rgbHex(color.rgb.r, color.rgb.g, color.rgb.b, color.rgb.a))}
32 | />
33 |
34 | )}
35 |
36 | );
37 | };
38 | export default ColorPicker;
39 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/region-select/index.tsx:
--------------------------------------------------------------------------------
1 | import { Select } from 'antd';
2 | import { Region, RegionsData } from '../../utilities/interfaces/misc';
3 |
4 | const { Option } = Select;
5 |
6 | interface LocationSelectProps {
7 | regions: RegionsData;
8 | currentRegion?: string;
9 | changeRegion: (region: Region) => void;
10 | }
11 |
12 | const LocationSelect = ({ regions, currentRegion, changeRegion }: LocationSelectProps) => {
13 | const handleChange = (value: string) => {
14 | changeRegion(regions[value]);
15 | };
16 |
17 | const defaultValue = Object.keys(regions).length === 0 ? '' : Object.keys(regions)[0];
18 |
19 | return (
20 | handleChange(val)}
26 | >
27 |
28 | Select a region
29 |
30 | {Object.keys(regions).map((key) => (
31 |
32 | {regions[key].displayName}
33 |
34 | ))}
35 |
36 | );
37 | };
38 |
39 | export default LocationSelect;
40 |
--------------------------------------------------------------------------------
/apps/backend/src/app.ts:
--------------------------------------------------------------------------------
1 | import express, { Application, Request, Response, NextFunction } from 'express';
2 | const app: Application = express();
3 | import cors from 'cors';
4 | import morgan from 'morgan';
5 | import CustomError from './errors/custom-error';
6 | import { ErrorResponse } from './utils/response-handler';
7 | import mainRoutes from './routes';
8 |
9 | console.log(`Environment: ${process.env.NODE_ENV}`);
10 |
11 | app.use(express.json(), morgan('dev'), cors());
12 |
13 | app.use('/api', mainRoutes);
14 |
15 | if (process.env.NODE_ENV === 'production') {
16 | app.use(express.static('./build'));
17 |
18 | const path = require('path');
19 | app.get('*', (req, res) => {
20 | res.sendFile(path.resolve(__dirname, 'build', 'index.html'));
21 | });
22 | }
23 |
24 | // All middlewares goes above this
25 |
26 | app.all('*', (req: Request, res: Response, next: NextFunction) => {
27 | const err = new CustomError('Non-existant route', 404);
28 | next(err);
29 | });
30 |
31 | app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
32 | if (err instanceof CustomError) {
33 | return res.status(err.statusCode).json(ErrorResponse(err));
34 | }
35 | return res.status(500).json(ErrorResponse(err));
36 | });
37 |
38 | export default app;
39 |
--------------------------------------------------------------------------------
/apps/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
25 |
26 | World Graph
27 |
28 |
29 | You need to enable JavaScript to run this app.
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/apps/backend/src/server.ts:
--------------------------------------------------------------------------------
1 | require('dotenv').config({ path: process.env.NODE_ENV === 'production' ? 'prod.env' : 'dev.env' });
2 | import app from './app';
3 | import { FullAdjacencyList, LandMarks } from './utils/interfaces/graph';
4 | import mongoose from 'mongoose';
5 | import { fetchAllRegionsFromDB } from './data-structures/adjacency-list';
6 | import { createLatLonIndexTree } from './data-structures/lat-lon-index-tree';
7 | import { createLandmarks } from './data-structures/landmark-list';
8 | import ContractionHierarchy from './data-structures/contraction-hierarchy';
9 |
10 | declare global {
11 | var fullAdjacencyList: FullAdjacencyList;
12 | var revFullAdjacencyList: FullAdjacencyList;
13 | var landmarks: LandMarks;
14 | var initialLandmark: string;
15 | var landmarksCount: number;
16 | var contractionHierarchy: ContractionHierarchy;
17 | }
18 |
19 | fetchAllRegionsFromDB().then(() => {
20 | console.log('Fetched all data from DB');
21 | createLatLonIndexTree();
22 | createLandmarks();
23 | const startTime = Date.now();
24 | const ch = new ContractionHierarchy();
25 | // console.log('CH class:', ch);
26 | global.contractionHierarchy = ch;
27 | console.log('CH preprocessing time:', (Date.now() - startTime) / 60000, 'mins');
28 | });
29 |
30 | // Variables
31 | const PORT = process.env.PORT || 5000;
32 |
33 | app.listen(PORT, () => console.log('Server is running at ' + PORT));
34 |
--------------------------------------------------------------------------------
/apps/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "world-graph-frontend",
3 | "description": "Frontend of the World Graph application",
4 | "version": "1.0.0",
5 | "private": true,
6 | "dependencies": {
7 | "antd": "^4.15.2",
8 | "axios": "^1.7.9",
9 | "leaflet": "^1.7.1",
10 | "react": "^17.0.2",
11 | "react-color": "^2.19.3",
12 | "react-dom": "^17.0.2",
13 | "react-leaflet": "^3.1.0",
14 | "react-leaflet-google-layer": "^2.0.3",
15 | "react-scripts": "4.0.3",
16 | "rgb-hex": "^3.0.0"
17 | },
18 | "scripts": {
19 | "dev": "react-app-rewired start",
20 | "build": "GENERATE_SOURCEMAP=false react-app-rewired build",
21 | "test": "react-app-rewired test",
22 | "eject": "react-scripts eject"
23 | },
24 | "eslintConfig": {
25 | "extends": [
26 | "react-app",
27 | "react-app/jest"
28 | ]
29 | },
30 | "browserslist": {
31 | "production": [
32 | ">0.2%",
33 | "not dead",
34 | "not op_mini all"
35 | ],
36 | "development": [
37 | "last 1 chrome version",
38 | "last 1 firefox version",
39 | "last 1 safari version"
40 | ]
41 | },
42 | "devDependencies": {
43 | "@types/leaflet": "^1.7.0",
44 | "@types/node": "^12.20.7",
45 | "@types/react": "^17.0.3",
46 | "@types/react-color": "^3.0.4",
47 | "@types/react-dom": "^17.0.3",
48 | "@types/rgb-hex": "^3.0.0",
49 | "react-app-rewire-babel-loader": "^0.1.1",
50 | "react-app-rewired": "^2.2.1",
51 | "typescript": "^4.2.4"
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/apps/backend/src/routes/index.ts:
--------------------------------------------------------------------------------
1 | import express from 'express';
2 | import * as mainController from '../controllers/main';
3 | import CustomError from '../errors/custom-error';
4 | import { SuccessResponse } from '../utils/response-handler';
5 | const router = express.Router();
6 |
7 | router.get('/', mainController.rootMainController);
8 |
9 | router.get('/getAllRegions', mainController.getAllRegions);
10 |
11 | router.get('/getAllAlgorithms', mainController.getAllAlgorithms);
12 |
13 | router.post('/execAlgorithm', mainController.execAlgorithm);
14 |
15 | // ? *** DEBUG START ***
16 | router.get('/getLandmarks/:region', async (req, res) => {
17 | try {
18 | const region: string = req.params.region;
19 | const landmarks = Object.keys(global.landmarks[region]).map((lKey) => {
20 | return { id: lKey, lat: global.landmarks[region][lKey].lat, lon: global.landmarks[region][lKey].lon };
21 | });
22 | res.json(SuccessResponse({ landmarks }, 'Fetched landmarks successfully'));
23 | } catch (err: any) {
24 | console.log(err.message);
25 | }
26 | });
27 |
28 | router.post('/deleteLandmark', async (req, res) => {
29 | try {
30 | const landmarkId = req.body.id;
31 | const region = req.body.region;
32 | if (global.landmarksCount == 0) throw new CustomError('No landmarks remaining in this region', 400);
33 | global.landmarksCount -= 1;
34 | delete global.landmarks[region][landmarkId];
35 | res.json(SuccessResponse({}, 'Deleted landmark successfully'));
36 | } catch (err) {
37 | console.log(err);
38 | }
39 | });
40 |
41 | // ? *** DEBUG END ***
42 |
43 | export default router;
44 |
--------------------------------------------------------------------------------
/apps/frontend/src/app.css:
--------------------------------------------------------------------------------
1 | /* The speed of the route is determined by the width of the stroke-dasharray and the animation-speed.
2 | In this example the route-animation will travel 1920px in 20 seconds.
3 | You can speed up or slow down the animation by adjusting either one of those CSS-values.
4 | */
5 |
6 | path.animate {
7 | stroke-dasharray: 1920;
8 | stroke-dashoffset: 1920;
9 | animation: dash 10s linear forwards;
10 | }
11 |
12 | @keyframes dash {
13 | to {
14 | stroke-dashoffset: 0;
15 | }
16 | }
17 |
18 | .leaflet-control-layers.leaflet-control {
19 | border: none;
20 | }
21 |
22 | .App {
23 | width: 100%;
24 | height: 100vh;
25 | }
26 |
27 | .options-box {
28 | position: absolute !important;
29 | top: 0px;
30 | left: 0px;
31 | padding: 14px;
32 | z-index: 500;
33 | display: flex;
34 | flex-wrap: wrap;
35 | align-items: center;
36 | width: 100%;
37 | background-color: #00000055;
38 | }
39 |
40 | .options-box > div,
41 | .options-box > button,
42 | .options-box > label,
43 | .options-box .ant-typography {
44 | margin-right: 1rem !important;
45 | margin-bottom: 2px;
46 | }
47 |
48 | .options-box .ant-typography {
49 | color: white;
50 | }
51 |
52 | .options-box > div:last-child {
53 | margin: 0 !important;
54 | }
55 |
56 | .color-picker {
57 | position: relative;
58 | }
59 |
60 | .color-picker .popover {
61 | position: absolute;
62 | z-index: 99999;
63 | }
64 |
65 | .color-picker .button {
66 | display: flex;
67 | align-items: center;
68 | }
69 |
70 | .color-picker .color-preview {
71 | width: 30px;
72 | height: 15px;
73 | border-radius: 3px;
74 | margin-right: 8px;
75 | }
76 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # World Graph
3 |
4 | **World Graph** is a routing application utilizing open data from OpenStreetMap. It allows the user to visualize the shortest path between two points on a map. It's a personal project developed mainly to learn and implement graphs and various shortest path algorithms.
5 | The XML Data obtained from the OpenStreetMap API is parsed into an adjacency list which is then used to run the algorithms.
6 |
7 | Algorithms used: (Ordered from fastest to slowest)
8 | - Bidirectional Dijkstra's with Contraction Hierarchies.
9 | - Bidirectional A-star with Landmarks
10 | - Bidirectional and Unidirectional A-star
11 | - Bidirectional and Unidirectional Dijkstra's
12 | - Breadth First Search (BFS)
13 |
14 | **Note:** Notice the variation in delay while visualizing the algorithms. Contraction Hierarchies are rendered almost instantly whereas Breadth First Takes can take upto a few seconds to render.
15 |
16 | **Note:** I have excluded Bellmanford's and Flyod Warshall's algorithms due to it's high time complexities.
17 | The algorithms are made to work with extremely large datasets. Due to hardware constraints in the deployment (Free Tier), only a selected section of the world map is available to test right now.
18 |
19 | 
20 |
21 | ## Authors
22 |
23 | - [@Bihan001](https://www.github.com/Bihan001)
24 |
25 |
26 | ## Run Locally
27 |
28 | 1. Clone the repository
29 | ```
30 | git clone https://github.com/Bihan001/world-graph.git
31 | ```
32 |
33 | 2. Go to the project directory
34 | ```
35 | cd world-graph
36 | ```
37 |
38 | 3. Build the image
39 | ```shell
40 | # Add --platform=linux/amd64 if building for linux through mac
41 | docker build . -t world-graph:1.0
42 | ```
43 |
44 | 4. Spin up a docker container through the built image
45 | ```
46 | docker run -d -e PORT=5000 -p 5000:5000 world-graph:1.0
47 | ```
48 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/landmark-scan.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { AdjacencyList } from '../utils/interfaces/graph';
3 |
4 | interface VWPair {
5 | vertex: string;
6 | weight: number;
7 | }
8 |
9 | const landmarkScanDijkstra = (startingKey: string, adjacencyList: AdjacencyList): { [prop: string]: number } => {
10 | const distance: { [prop: string]: number } = {};
11 |
12 | const set = new SortedSet(
13 | [],
14 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
15 | (a, b) => {
16 | if (a.weight < b.weight) return -1;
17 | if (a.weight > b.weight) return 1;
18 | if (a.vertex !== b.vertex) return -1;
19 | return 0;
20 | }
21 | );
22 |
23 | distance[startingKey] = 0;
24 | set.add({ vertex: startingKey, weight: 0 });
25 |
26 | while (set.length > 0) {
27 | const currentObj = set.min();
28 | if (!currentObj) break;
29 | const current = currentObj.vertex;
30 | set.delete(currentObj);
31 |
32 | if (!current) break;
33 |
34 | for (let i = 0; i < adjacencyList[current].adj.length; i++) {
35 | const neighbourKey: string = adjacencyList[current].adj[i].toString();
36 | const currWeight: number = adjacencyList[current].w[i];
37 | const newWeight: number = distance[current] + currWeight;
38 | if (isNaN(distance[neighbourKey])) {
39 | distance[neighbourKey] = newWeight;
40 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
41 | } else if (newWeight < distance[neighbourKey]) {
42 | set.delete({ vertex: neighbourKey, weight: distance[neighbourKey] });
43 | distance[neighbourKey] = newWeight;
44 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
45 | }
46 | }
47 | }
48 | // landmarkScanDijkstra complete
49 |
50 | return distance;
51 | };
52 |
53 | export default landmarkScanDijkstra;
54 |
--------------------------------------------------------------------------------
/apps/backend/src/controllers/main.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { SuccessResponse } from '../utils/response-handler';
3 | import catchAsync from '../utils/catch-async';
4 | import CustomError from '../errors/custom-error';
5 | import { regionsData } from '../data/regions';
6 | import { algorithmsData } from '../data/algorithms';
7 | import { LatLon } from '../utils/interfaces/graph';
8 | import { handleAlgorithmExec } from '../algorithms/algorithm-handler';
9 |
10 | // @method GET
11 | export const rootMainController = catchAsync(async (req: Request, res: Response) => {
12 | res.status(200).json(SuccessResponse({}, 'Main Route is up and running!'));
13 | });
14 |
15 | // @method GET
16 | export const getAllRegions = catchAsync(async (req: Request, res: Response) => {
17 | res.status(200).json(SuccessResponse(regionsData, 'Fetched regions successfully'));
18 | });
19 |
20 | // @method GET
21 | export const getAllAlgorithms = catchAsync(async (req: Request, res: Response) => {
22 | res.status(200).json(SuccessResponse(algorithmsData, 'Fetched regions successfully'));
23 | });
24 |
25 | // @method POST
26 | export const execAlgorithm = catchAsync(async (req: Request, res: Response) => {
27 | const region: string = req.body.region;
28 | const algorithm: string = req.body.algorithm;
29 | const pointA: LatLon = req.body.pointA;
30 | const pointB: LatLon = req.body.pointB;
31 |
32 | if (!region) throw new CustomError('A region must be selected', 400);
33 | if (!algorithm) throw new CustomError('An algorithm must be selected', 400);
34 | if (!pointA || !pointB || isNaN(pointA.lat) || isNaN(pointA.lon) || isNaN(pointB.lat) || isNaN(pointB.lon)) {
35 | throw new CustomError('Coordinates of both points must be present', 400);
36 | }
37 |
38 | const result = handleAlgorithmExec(region, algorithm, pointA, pointB);
39 | if (result instanceof Error) throw new CustomError(result.message, 500);
40 | const { edges, allEdges } = result;
41 | res.status(200).json(SuccessResponse({ edges, allEdges }, 'Success'));
42 | });
43 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/scan-select-landmarks.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { AdjacencyList } from '../utils/interfaces/graph';
3 |
4 | interface VWPair {
5 | vertex: string;
6 | weight: number;
7 | }
8 |
9 | const scanAndSelectLandmarks = (adjacencyList: AdjacencyList, landmarkIds: Array): string => {
10 | const distance: { [prop: string]: number } = {};
11 |
12 | const set = new SortedSet(
13 | [],
14 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
15 | (a, b) => {
16 | if (a.weight < b.weight) return -1;
17 | if (a.weight > b.weight) return 1;
18 | if (a.vertex !== b.vertex) return -1;
19 | return 0;
20 | }
21 | );
22 |
23 | landmarkIds.map((id) => {
24 | distance[id] = 0;
25 | set.add({ vertex: id, weight: 0 });
26 | });
27 |
28 | while (set.length > 0) {
29 | const currentObj = set.min();
30 | if (!currentObj) break;
31 | const current = currentObj.vertex;
32 | set.delete(currentObj);
33 |
34 | if (!current) break;
35 |
36 | for (let i = 0; i < adjacencyList[current].adj.length; i++) {
37 | const neighbourKey: string = adjacencyList[current].adj[i].toString();
38 | const currWeight: number = adjacencyList[current].w[i];
39 | const newWeight: number = distance[current] + currWeight;
40 | if (isNaN(distance[neighbourKey])) {
41 | distance[neighbourKey] = newWeight;
42 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
43 | } else if (newWeight < distance[neighbourKey]) {
44 | set.delete({ vertex: neighbourKey, weight: distance[neighbourKey] });
45 | distance[neighbourKey] = newWeight;
46 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
47 | }
48 | }
49 | }
50 | // scanAndSelectLandmarks complete
51 |
52 | let maxVal = -9999999;
53 | let vId = '';
54 |
55 | Object.keys(distance).map((id) => {
56 | if (distance[id] > maxVal) {
57 | maxVal = distance[id];
58 | vId = id;
59 | }
60 | });
61 |
62 | return vId;
63 | };
64 |
65 | export default scanAndSelectLandmarks;
66 |
--------------------------------------------------------------------------------
/apps/frontend/src/about.tsx:
--------------------------------------------------------------------------------
1 | import { Button, Modal } from 'antd';
2 |
3 | interface AboutInterface {
4 | isModalVisible: boolean;
5 | handleClose: () => void;
6 | }
7 |
8 | const About: React.FC = ({ isModalVisible, handleClose }) => {
9 | return (
10 |
17 | Close
18 | ,
19 | ]}
20 | >
21 |
22 | World Graph is a routing application utilizing open data from OpenStreetMap. It allows the user to visualize the shortest
23 | path between two points on a map. It's a personal project developed mainly to learn and implement graphs and various shortest path
24 | algorithms.
25 | The XML Data obtained from the OpenStreetMap API is parsed into an adjacency list which is then used to run the algorithms.
26 |
27 | Algorithms used: (Ordered from fastest to slowest)
28 |
29 |
30 | Bidirectional Dijkstra's with Contraction Hierarchies.
31 | Bidirectional A-star with Landmarks
32 | Bidirectional and Unidirectional A-star
33 | Bidirectional and Unidirectional Dijkstra's
34 | Breadth First Search (BFS)
35 |
36 | Note: Notice the variation in delay while visualizing the algorithms. Contraction Hierarchies are rendered almost instantly
37 | whereas Breadth First Takes can take upto a few seconds to render.
38 |
39 | Note: I have excluded Bellmanford's and Flyod Warshall's algorithms due to it's high time complexities.
40 |
41 | The algorithms are made to work with extremely large datasets. Due to hardware constraints in the deployment (Free Tier), only a
42 | selected section of the world map is available to test right now.
43 |
44 |
45 |
46 | );
47 | };
48 |
49 | export default About;
50 |
--------------------------------------------------------------------------------
/apps/preprocess/create-adj-list-from-osm.js:
--------------------------------------------------------------------------------
1 | const osmread = require('osm-read');
2 |
3 | const nodes = {};
4 | const graph = {};
5 | let cnt = 1;
6 | const args = process.argv.slice(2);
7 | if (args.length < 1) throw new Error('Enter file path');
8 | const parser = osmread.parse({
9 | filePath: args[0],
10 | endDocument: function () {
11 | // console.log(nodes);
12 | console.log(JSON.stringify(graph));
13 | // console.log('Document End');
14 | },
15 | bounds: function (bounds) {
16 | // console.log('Bounds: ' + JSON.stringify(bounds));
17 | },
18 | node: function (node) {
19 | // console.log('node: ' + JSON.stringify(node));
20 | if (nodes[node.id]) return;
21 | nodes[node.id] = cnt;
22 | graph[cnt] = { lat: node.lat, lon: node.lon, adj: [], w: [] };
23 | cnt++;
24 | },
25 | way: function (way) {
26 | // console.log('way: ' + JSON.stringify(way));
27 | if (way.nodeRefs.length < 2) return;
28 | for (let i = 0; i < way.nodeRefs.length - 1; i++) {
29 | let u = nodes[way.nodeRefs[i]];
30 | let v = nodes[way.nodeRefs[i + 1]];
31 | if (!u || !v || u == v) continue;
32 | const weight = calc_distance(graph[u].lat, graph[u].lon, graph[v].lat, graph[v].lon);
33 | if (graph[u].adj.indexOf(v) == -1) {
34 | graph[u].adj.push(v);
35 | graph[u].w.push(weight);
36 | }
37 | if (graph[v].adj.indexOf(u) == -1) {
38 | graph[v].adj.push(u);
39 | graph[v].w.push(weight);
40 | }
41 | }
42 | },
43 | relation: function (relation) {
44 | // console.log('relation: ' + JSON.stringify(relation));
45 | },
46 | error: function (msg) {
47 | console.log('error: ' + msg);
48 | },
49 | });
50 |
51 | function calc_distance(lat1, lon1, lat2, lon2) {
52 | const R = 6373.0; // km
53 | lat1 = (lat1 * Math.PI) / 180.0;
54 | lon1 = (lon1 * Math.PI) / 180.0;
55 | lat2 = (lat2 * Math.PI) / 180.0;
56 | lon2 = (lon2 * Math.PI) / 180.0;
57 | const dLat = lat2 - lat1;
58 | const dLon = lon2 - lon1;
59 | const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
60 | const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
61 | const d = R * c;
62 | return +(d * 1000).toFixed(5);
63 | }
64 |
--------------------------------------------------------------------------------
/apps/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/bfs.ts:
--------------------------------------------------------------------------------
1 | import { Edge, AdjacencyList } from '../utils/interfaces/graph';
2 |
3 | const bfs = (
4 | targetVertices: Array,
5 | adjacencyList: AdjacencyList
6 | ): { allEdges: Array; edges: Array } => {
7 | const visited: { [prop: string]: boolean } = {};
8 | const parent: { [prop: string]: { vertex: string; weight: number } } = {};
9 | const edges: Array = [];
10 | const allEdges: Array = [];
11 |
12 | Object.keys(adjacencyList).map((key) => (visited[key] = false));
13 | const queue: Array<{ vertex: string; weight: number }> = [];
14 |
15 | let startingKey: string = targetVertices[0];
16 | let endingKey: string = targetVertices[1];
17 |
18 | visited[startingKey] = true;
19 | parent[startingKey] = { vertex: startingKey, weight: 0 };
20 | queue.push({ vertex: startingKey, weight: 0 });
21 |
22 | while (queue.length > 0) {
23 | const top: { vertex: string; weight: number } | null = queue.shift() || null;
24 | if (!top) break;
25 |
26 | allEdges.push({
27 | src: [adjacencyList[parent[top.vertex].vertex].lat, adjacencyList[parent[top.vertex].vertex].lon],
28 | dest: [adjacencyList[top.vertex].lat, adjacencyList[top.vertex].lon],
29 | weight: parent[top.vertex].weight,
30 | });
31 |
32 | if (top?.vertex === endingKey) break;
33 |
34 | for (let i = 0; i < adjacencyList[top.vertex].adj.length; i++) {
35 | const neighbourKey: string = adjacencyList[top.vertex].adj[i].toString();
36 | if (!visited[neighbourKey]) {
37 | visited[neighbourKey] = true;
38 | queue.push({ vertex: neighbourKey, weight: adjacencyList[top.vertex].w[i] });
39 | parent[neighbourKey] = { vertex: top.vertex, weight: adjacencyList[top.vertex].w[i] };
40 | }
41 | }
42 | }
43 | // bfs complete
44 |
45 | if (!parent[endingKey]) return { allEdges, edges };
46 |
47 | while (endingKey !== startingKey) {
48 | edges.push({
49 | src: [adjacencyList[parent[endingKey].vertex].lat, adjacencyList[parent[endingKey].vertex].lon],
50 | dest: [adjacencyList[endingKey].lat, adjacencyList[endingKey].lon],
51 | weight: parent[endingKey].weight,
52 | });
53 | endingKey = parent[endingKey].vertex;
54 | }
55 |
56 | edges.reverse();
57 |
58 | return { allEdges, edges };
59 | };
60 |
61 | export default bfs;
62 |
--------------------------------------------------------------------------------
/apps/backend/src/data/algorithms.ts:
--------------------------------------------------------------------------------
1 | import { AlgorithmsData } from '../utils/interfaces/misc';
2 |
3 | export const algorithmsData: AlgorithmsData = {
4 | contractionHierarchy: {
5 | key: 'contractionHierarchy',
6 | displayName: 'Contraction Hierarchy',
7 | timeComplexity: 'O(V + E)',
8 | spaceComplexity: 'O(V)',
9 | description: 'lorem ipsum',
10 | requirements: 'Weighted',
11 | },
12 | bfs: {
13 | key: 'bfs',
14 | displayName: 'Breadth First Search',
15 | timeComplexity: 'O(V + E)',
16 | spaceComplexity: 'O(V)',
17 | description: 'lorem ipsum',
18 | requirements: 'Unweighted, Acyclic',
19 | },
20 | // dfs: {
21 | // key: 'dfs',
22 | // displayName: 'Depth First Search',
23 | // timeComplexity: 'O(V + E)',
24 | // spaceComplexity: 'O(V)',
25 | // description: 'lorem ipsum',
26 | // requirements: 'Unweighted, Acyclic',
27 | // },
28 | dijkstra: {
29 | key: 'dijkstra',
30 | displayName: "Dijkstra's Algorithm",
31 | timeComplexity: 'O(V.log(E))',
32 | spaceComplexity: 'O(V)',
33 | description: 'SSSP lorem ipsum',
34 | requirements: 'Weighted, No negative edge cycles',
35 | },
36 | bidirectionalDijkstra: {
37 | key: 'bidirectionalDijkstra',
38 | displayName: "Bidirectional Dijkstra's Algorithm",
39 | timeComplexity: 'O(V.log(E)/2)',
40 | spaceComplexity: 'O(V)',
41 | description: 'SSSP lorem ipsum',
42 | requirements: 'Weighted, No negative edge cycles',
43 | },
44 | aStar: {
45 | key: 'aStar',
46 | displayName: 'A* Algorithm',
47 | timeComplexity: 'O(V.log(E))',
48 | spaceComplexity: 'O(V)',
49 | description: 'SSSP lorem ipsum',
50 | requirements: 'Weighted, No negative edge cycles',
51 | },
52 | bidirectionalAStar: {
53 | key: 'bidirectionalAStar',
54 | displayName: 'Bidirectional A* Algorithm',
55 | timeComplexity: 'O(V.log(E))',
56 | spaceComplexity: 'O(V)',
57 | description: 'SSSP lorem ipsum',
58 | requirements: 'Weighted, No negative edge cycles',
59 | },
60 | bidirectionalAStarALT: {
61 | key: 'bidirectionalAStarALT',
62 | displayName: 'ALT Bidirectional A* Algorithm',
63 | timeComplexity: 'O(V.log(E))',
64 | spaceComplexity: 'O(V)',
65 | description: 'SSSP lorem ipsum',
66 | requirements: 'Weighted, No negative edge cycles',
67 | },
68 | // bellmanFord: {
69 | // key: 'bellmanFord',
70 | // displayName: "Bellman Ford's Algorithm",
71 | // timeComplexity: 'O(V.E)',
72 | // spaceComplexity: 'O(V)',
73 | // description: 'SSSP lorem ipsum',
74 | // requirements: 'Weighted',
75 | // },
76 | // floydWarshall: {
77 | // key: 'floydWarshall',
78 | // displayName: "Floyd Warshall's Algorithm",
79 | // timeComplexity: 'O(V^3)',
80 | // spaceComplexity: 'O(V)',
81 | // description: 'APSP lorem ipsum',
82 | // requirements: 'Weighted',
83 | // },
84 | };
85 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/dijkstra.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { Edge, AdjacencyList } from '../utils/interfaces/graph';
3 |
4 | interface VWPair {
5 | vertex: string;
6 | weight: number;
7 | }
8 |
9 | const dijkstra = (
10 | targetVertices: Array,
11 | adjacencyList: AdjacencyList
12 | ): { allEdges: Array; edges: Array } => {
13 | const parent: { [prop: string]: VWPair } = {};
14 | const distance: { [prop: string]: number } = {};
15 | const edges: Array = [];
16 | const allEdges: Array = [];
17 | // Object.keys(adjacencyList).map((key) => (distance[key] = Infinity));
18 |
19 | let startingKey: string = targetVertices[0];
20 | let endingKey: string = targetVertices[1];
21 |
22 | const set = new SortedSet(
23 | [],
24 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
25 | (a, b) => {
26 | if (a.weight < b.weight) return -1;
27 | if (a.weight > b.weight) return 1;
28 | if (a.vertex !== b.vertex) return -1;
29 | return 0;
30 | }
31 | );
32 |
33 | distance[startingKey] = 0;
34 | parent[startingKey] = { vertex: startingKey, weight: 0 };
35 | set.add({ vertex: startingKey, weight: 0 });
36 |
37 | while (set.length > 0) {
38 | const currentObj = set.min();
39 | if (!currentObj) break;
40 | const current = currentObj.vertex;
41 | set.delete(currentObj);
42 |
43 | if (!current) break;
44 |
45 | allEdges.push({
46 | src: [adjacencyList[parent[current].vertex].lat, adjacencyList[parent[current].vertex].lon],
47 | dest: [adjacencyList[current].lat, adjacencyList[current].lon],
48 | weight: parent[current].weight,
49 | });
50 |
51 | if (current === endingKey) break;
52 |
53 | for (let i = 0; i < adjacencyList[current].adj.length; i++) {
54 | const neighbourKey: string = adjacencyList[current].adj[i].toString();
55 | const currWeight: number = adjacencyList[current].w[i];
56 | const newWeight: number = distance[current] + currWeight;
57 | if (isNaN(distance[neighbourKey])) {
58 | distance[neighbourKey] = newWeight;
59 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
60 | parent[neighbourKey] = { vertex: current, weight: adjacencyList[current].w[i] };
61 | } else if (newWeight < distance[neighbourKey]) {
62 | set.delete({ vertex: neighbourKey, weight: distance[neighbourKey] });
63 | distance[neighbourKey] = newWeight;
64 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] });
65 | parent[neighbourKey] = { vertex: current, weight: adjacencyList[current].w[i] };
66 | }
67 | }
68 | }
69 | // dijkstra complete
70 |
71 | if (!parent[endingKey]) return { allEdges, edges };
72 |
73 | while (endingKey !== startingKey) {
74 | edges.push({
75 | src: [adjacencyList[parent[endingKey].vertex].lat, adjacencyList[parent[endingKey].vertex].lon],
76 | dest: [adjacencyList[endingKey].lat, adjacencyList[endingKey].lon],
77 | weight: parent[endingKey].weight,
78 | });
79 | endingKey = parent[endingKey].vertex;
80 | }
81 |
82 | edges.reverse();
83 |
84 | return { allEdges, edges };
85 | };
86 |
87 | export default dijkstra;
88 |
--------------------------------------------------------------------------------
/apps/backend/src/data-structures/landmark-list.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import { AdjacencyList, FullAdjacencyList, LandMark } from '../utils/interfaces/graph';
3 | import { regionsData } from '../data/regions';
4 | import { addPointToAdjacencyList, deletePointFromAdjacencyList, findNeighboursOfPoints } from '../algorithms/algorithm-handler';
5 | import landmarkScanDijkstra from '../algorithms/landmark-scan';
6 | import scanAndSelectLandmarks from '../algorithms/scan-select-landmarks';
7 |
8 | global.landmarks = {};
9 |
10 | export const createLandmarks = () => {
11 | // readAllLandmarksFromDisk();
12 | createAndWriteAllLandmarks();
13 | };
14 |
15 | const readAllLandmarksFromDisk = () => {
16 | const path = 'landmarks.json';
17 | if (!fs.existsSync(path)) return false;
18 | global.landmarks = JSON.parse(fs.readFileSync(path, 'utf-8'));
19 | console.log('Landmarks loaded');
20 | return true;
21 | };
22 |
23 | const createAndWriteAllLandmarks = () => {
24 | const fullAdjacencyList: FullAdjacencyList = global.fullAdjacencyList;
25 | const startTimer = Date.now();
26 | if (!readAllLandmarksFromDisk()) {
27 | Object.keys(fullAdjacencyList).map((region, idx) => {
28 | // const stream = fs.createWriteStream(`/home/bihan/codes/landmarks-${region}.json`, { flags: 'a' });
29 | // stream.write('[');
30 | const adjacencyList: AdjacencyList = fullAdjacencyList[region];
31 | const adjBounds = regionsData[region].boundary;
32 | let str = '';
33 | const landmarkIds: Array = [];
34 | const l = Object.keys(adjacencyList).length;
35 | const initialRandomLandmarkId = Object.keys(adjacencyList)[Math.floor(Math.random() * l)];
36 | console.log('initial landmark id:', initialRandomLandmarkId);
37 | global.initialLandmark = initialRandomLandmarkId;
38 | const n = 23;
39 | global.landmarksCount = n;
40 | landmarkIds.push(initialRandomLandmarkId);
41 | for (let i = 0; i < n - 1; i++) {
42 | selectLandmarks(adjacencyList, landmarkIds);
43 | }
44 | landmarkIds.map((id) => {
45 | const distances = landmarkScanDijkstra(id, adjacencyList);
46 | const landmark: LandMark = {
47 | lat: adjacencyList[id].lat,
48 | lon: adjacencyList[id].lon,
49 | distances,
50 | };
51 | if (!global.landmarks[region]) global.landmarks[region] = {};
52 | global.landmarks[region][id] = landmark;
53 | });
54 | landmarkIds.map((id) => {
55 | console.log({ lat: adjacencyList[id].lat, lon: adjacencyList[id].lon });
56 | });
57 | });
58 | fs.writeFileSync('landmarks.json', JSON.stringify(global.landmarks));
59 | }
60 | console.log('Landmarks ready!', `${(Date.now() - startTimer) / 60000} mins`);
61 | };
62 |
63 | const createAndFillLandmark = (adjacencyList: AdjacencyList, region: string, lat: number, lon: number) => {
64 | const { key, vertex, closestPoints } = findNeighboursOfPoints({ lat, lon }, 'large');
65 | addPointToAdjacencyList(adjacencyList, key, vertex, closestPoints);
66 | const distances = landmarkScanDijkstra(key, adjacencyList);
67 | deletePointFromAdjacencyList(adjacencyList, key, closestPoints);
68 | const landmark: LandMark = { lat, lon, distances };
69 | if (!global.landmarks[region]) global.landmarks[region] = {};
70 | global.landmarks[region][key] = landmark;
71 | return `"${key}":${JSON.stringify(landmark)}`;
72 | };
73 |
74 | const selectLandmarks = (adjacencyList: AdjacencyList, landmarkIds: Array) => {
75 | landmarkIds.push(scanAndSelectLandmarks(adjacencyList, landmarkIds));
76 | };
77 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/a-star.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { heuristic } from '../utils/functions';
3 | import { Edge, AdjacencyList, LatLon } from '../utils/interfaces/graph';
4 |
5 | interface VWPair {
6 | vertex: string;
7 | weight: number;
8 | }
9 |
10 | const aStar = (targetVertices: Array, adjacencyList: AdjacencyList): { allEdges: Array; edges: Array } => {
11 | const parent: { [prop: string]: VWPair } = {};
12 | const distance: { [prop: string]: number } = {};
13 | const visited: { [prop: string]: boolean } = {};
14 | const edges: Array = [];
15 | const allEdges: Array = [];
16 | // Object.keys(adjacencyList).map((key) => (distance[key] = Infinity));
17 |
18 | let startingKey: string = targetVertices[0];
19 | let endingKey: string = targetVertices[1];
20 |
21 | const set = new SortedSet(
22 | [],
23 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
24 | (a, b) => {
25 | if (a.weight < b.weight) return -1;
26 | if (a.weight > b.weight) return 1;
27 | if (a.vertex !== b.vertex) return -1;
28 | return 0;
29 | }
30 | );
31 |
32 | distance[startingKey] = 0;
33 | visited[startingKey] = true;
34 | parent[startingKey] = { vertex: startingKey, weight: 0 };
35 | const hs = heuristic(
36 | { lat: adjacencyList[startingKey].lat, lon: adjacencyList[startingKey].lon },
37 | { lat: adjacencyList[endingKey].lat, lon: adjacencyList[endingKey].lon }
38 | );
39 | set.add({ vertex: startingKey, weight: hs });
40 |
41 | while (set.length > 0) {
42 | const currentObj = set.min();
43 | if (!currentObj) break;
44 | const current = currentObj.vertex;
45 | set.delete(currentObj);
46 |
47 | if (!current) break;
48 |
49 | visited[current] = true;
50 |
51 | allEdges.push({
52 | src: [adjacencyList[parent[current].vertex].lat, adjacencyList[parent[current].vertex].lon],
53 | dest: [adjacencyList[current].lat, adjacencyList[current].lon],
54 | weight: parent[current].weight,
55 | });
56 |
57 | if (current === endingKey) {
58 | console.log('breaked');
59 | break;
60 | }
61 |
62 | for (let i = 0; i < adjacencyList[current].adj.length; i++) {
63 | const neighbourKey: string = adjacencyList[current].adj[i].toString();
64 | const currWeight: number = adjacencyList[current].w[i];
65 | const newWeight: number = distance[current] + currWeight;
66 |
67 | if (visited[neighbourKey]) continue;
68 |
69 | if (isNaN(distance[neighbourKey]) || newWeight < distance[neighbourKey]) {
70 | const h = heuristic(
71 | { lat: adjacencyList[neighbourKey].lat, lon: adjacencyList[neighbourKey].lon },
72 | { lat: adjacencyList[endingKey].lat, lon: adjacencyList[endingKey].lon }
73 | );
74 | distance[neighbourKey] = newWeight;
75 | set.add({ vertex: neighbourKey, weight: distance[neighbourKey] + h });
76 | parent[neighbourKey] = { vertex: current, weight: adjacencyList[current].w[i] };
77 | }
78 | }
79 | }
80 | // aStar complete
81 |
82 | if (!parent[endingKey]) return { allEdges, edges };
83 |
84 | while (endingKey !== startingKey) {
85 | edges.push({
86 | src: [adjacencyList[parent[endingKey].vertex].lat, adjacencyList[parent[endingKey].vertex].lon],
87 | dest: [adjacencyList[endingKey].lat, adjacencyList[endingKey].lon],
88 | weight: parent[endingKey].weight,
89 | });
90 | endingKey = parent[endingKey].vertex;
91 | }
92 |
93 | edges.reverse();
94 |
95 | return { allEdges, edges };
96 | };
97 |
98 | export default aStar;
99 |
--------------------------------------------------------------------------------
/osm_parser.py:
--------------------------------------------------------------------------------
1 | import xml.etree.ElementTree as ET
2 | import json
3 | from random import randrange
4 | from math import sin, cos, sqrt, atan2, radians
5 | import sys
6 |
7 | data = {}
8 | node_info = {}
9 |
10 |
11 | def calc_distance(lat1, lon1, lat2, lon2):
12 | R = 6373.0
13 | lat1 = radians(lat1)
14 | lon1 = radians(lon1)
15 | lat2 = radians(lat2)
16 | lon2 = radians(lon2)
17 | dlon = lon2 - lon1
18 | dlat = lat2 - lat1
19 | a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
20 | c = 2 * atan2(sqrt(a), sqrt(1 - a))
21 | distance = R * c
22 | return distance
23 |
24 |
25 |
26 | def parse_way(way):
27 | nodes = []
28 | for w in way:
29 | if w.tag == 'nd' and w.attrib['ref'] in node_info:
30 | nodes.append(w.attrib['ref'])
31 | return nodes
32 |
33 |
34 | def create_node(nodes, i):
35 | length = len(nodes)
36 | if i == 0:
37 | pre_node = None
38 | next_node = int(nodes[i]) # deleted +1
39 | elif i == length-1:
40 | pre_node = int(nodes[i-1])
41 | next_node = None
42 | else:
43 | pre_node = int(nodes[i-1])
44 | next_node = int(nodes[i+1])
45 | return pre_node, nodes[i], next_node
46 |
47 |
48 | def create_data_info(current_node):
49 | lat, lon = node_info[current_node]['lat'], node_info[current_node]['lon']
50 | data[current_node] = {'lat': lat, 'lon': lon, 'adj': [], 'w': []}
51 |
52 |
53 | def add_nodes(nodes):
54 | length = len(nodes)
55 | for i in range(length):
56 | pre_node, current_node, next_node = create_node(nodes, i)
57 | if pre_node:
58 | pre_node = str(pre_node)
59 | if current_node:
60 | current_node = str(current_node)
61 | if next_node:
62 | next_node = str(next_node)
63 | if current_node not in data.keys():
64 | create_data_info(current_node)
65 | if pre_node and pre_node not in data.keys():
66 | create_data_info(pre_node)
67 | if next_node and next_node not in data.keys():
68 | create_data_info(next_node)
69 | if pre_node and pre_node not in data[current_node]['adj'] and str(current_node) != str(pre_node) and pre_node != next_node:
70 | data[current_node]['adj'].append(int(pre_node))
71 | data[pre_node]['adj'].append(int(current_node))
72 | weight = calc_distance(data[pre_node]['lat'], data[pre_node]['lon'], data[current_node]['lat'], data[current_node]['lon']) * 1000
73 | weight = round(weight, 5)
74 | data[current_node]['w'].append(weight)
75 | data[pre_node]['w'].append(weight)
76 | if next_node and next_node not in data[current_node]['adj'] and str(current_node) != str(next_node) and next_node != pre_node:
77 | data[current_node]['adj'].append(int(next_node))
78 | data[next_node]['adj'].append(int(current_node))
79 | weight = calc_distance(data[next_node]['lat'], data[next_node]['lon'], data[current_node]['lat'], data[current_node]['lon']) * 1000
80 | weight = round(weight, 5)
81 | data[current_node]['w'].append(weight)
82 | data[next_node]['w'].append(weight)
83 |
84 |
85 | def create_nodes_info(node):
86 | node_id, lat, lon = node.attrib['id'], node.attrib['lat'], node.attrib['lon']
87 | node_info[node_id] = {'lat': float(lat), 'lon': float(lon)}
88 |
89 |
90 | def store_json(data):
91 | print(json.dumps(data))
92 | # with open('kolkata-large1.json', 'w') as f:
93 | # f.write(json.dumps(data))
94 |
95 |
96 | def main():
97 | # file_path = input()
98 | if(len(sys.argv) < 2):
99 | raise Exception("Please provide the file path")
100 | file_path = sys.argv[1]
101 | tree = ET.ElementTree(file=file_path)
102 | root = tree.getroot()
103 |
104 | for child_root in root:
105 | if child_root.tag == 'node':
106 | create_nodes_info(child_root)
107 |
108 | for child_root in root:
109 | if child_root.tag == 'way':
110 | nodes = parse_way(child_root)
111 | add_nodes(nodes)
112 |
113 | store_json(data)
114 |
115 |
116 | if __name__ == "__main__":
117 | main()
--------------------------------------------------------------------------------
/apps/backend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./dist" /* Redirect output structure to the directory. */,
18 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true /* Enable all strict type-checking options. */,
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
43 |
44 | /* Module Resolution Options */
45 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
46 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
52 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
55 |
56 | /* Source Map Options */
57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
61 |
62 | /* Experimental Options */
63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
65 | /* Advanced Options */
66 | "skipLibCheck": true /* Skip type checking of declaration files. */,
67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/bidirectional-dijkstra.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { Edge, AdjacencyList } from '../utils/interfaces/graph';
3 |
4 | interface VWPair {
5 | vertex: string;
6 | weight: number;
7 | }
8 |
9 | const bidirectionalDijkstra = (
10 | targetVertices: Array,
11 | adjacencyList: AdjacencyList,
12 | revAdjacencyList: AdjacencyList
13 | ): { allEdges: Array; edges: Array } => {
14 | const parentF: { [prop: string]: VWPair } = {};
15 | const parentR: { [prop: string]: VWPair } = {};
16 | const distanceF: { [prop: string]: number } = {};
17 | const distanceR: { [prop: string]: number } = {};
18 |
19 | const procF: Set = new Set();
20 | const procR: Set = new Set();
21 |
22 | const setF = new SortedSet(
23 | [],
24 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
25 | (a, b) => {
26 | if (a.weight < b.weight) return -1;
27 | if (a.weight > b.weight) return 1;
28 | if (a.vertex !== b.vertex) return -1;
29 | return 0;
30 | }
31 | );
32 |
33 | const setR = new SortedSet(
34 | [],
35 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
36 | (a, b) => {
37 | if (a.weight < b.weight) return -1;
38 | if (a.weight > b.weight) return 1;
39 | if (a.vertex !== b.vertex) return -1;
40 | return 0;
41 | }
42 | );
43 |
44 | const edges: Array = [];
45 | const allEdges: Array = [];
46 |
47 | // Can be cached maybe
48 | // Object.keys(adjacencyList).map((key) => (distanceF[key] = Infinity));
49 | // Object.keys(adjacencyList).map((key) => (distanceR[key] = Infinity));
50 |
51 | let startingKey: string = targetVertices[0];
52 | let endingKey: string = targetVertices[1];
53 |
54 | distanceF[startingKey] = 0;
55 | distanceR[endingKey] = 0;
56 | parentF[startingKey] = { vertex: startingKey, weight: 0 };
57 | parentR[endingKey] = { vertex: endingKey, weight: 0 };
58 | setF.add({ vertex: startingKey, weight: 0 });
59 | setR.add({ vertex: endingKey, weight: 0 });
60 |
61 | while (setF.length > 0 && setR.length > 0) {
62 | const currentFObj = setF.min();
63 | if (!currentFObj) break;
64 | const currentF = currentFObj.vertex;
65 | setF.delete(currentFObj);
66 |
67 | for (let i = 0; i < adjacencyList[currentF].adj.length; i++) {
68 | const neighbourKey: string = adjacencyList[currentF].adj[i].toString();
69 | const currWeight: number = adjacencyList[currentF].w[i];
70 | const newWeight: number = distanceF[currentF] + currWeight;
71 | if (isNaN(distanceF[neighbourKey])) {
72 | distanceF[neighbourKey] = newWeight;
73 | setF.add({ vertex: neighbourKey, weight: distanceF[neighbourKey] });
74 | parentF[neighbourKey] = { vertex: currentF, weight: adjacencyList[currentF].w[i] };
75 | } else if (newWeight < distanceF[neighbourKey]) {
76 | setF.delete({ vertex: neighbourKey, weight: distanceF[neighbourKey] });
77 | distanceF[neighbourKey] = newWeight;
78 | setF.add({ vertex: neighbourKey, weight: distanceF[neighbourKey] });
79 | parentF[neighbourKey] = { vertex: currentF, weight: adjacencyList[currentF].w[i] };
80 | }
81 | }
82 |
83 | procF.add(currentF);
84 |
85 | allEdges.push({
86 | src: [adjacencyList[parentF[currentF].vertex].lat, adjacencyList[parentF[currentF].vertex].lon],
87 | dest: [adjacencyList[currentF].lat, adjacencyList[currentF].lon],
88 | weight: parentF[currentF].weight,
89 | });
90 |
91 | if (procR.has(currentF)) {
92 | break;
93 | }
94 |
95 | const currentRObj = setR.min();
96 | if (!currentRObj) break;
97 | const currentR = currentRObj.vertex;
98 | setR.delete(currentRObj);
99 |
100 | for (let i = 0; i < revAdjacencyList[currentR].adj.length; i++) {
101 | const neighbourKey: string = revAdjacencyList[currentR].adj[i].toString();
102 | const currWeight: number = revAdjacencyList[currentR].w[i];
103 | const newWeight: number = distanceR[currentR] + currWeight;
104 | if (isNaN(distanceR[neighbourKey])) {
105 | distanceR[neighbourKey] = newWeight;
106 | setR.add({ vertex: neighbourKey, weight: distanceR[neighbourKey] });
107 | parentR[neighbourKey] = { vertex: currentR, weight: revAdjacencyList[currentR].w[i] };
108 | } else if (newWeight < distanceR[neighbourKey]) {
109 | setR.delete({ vertex: neighbourKey, weight: distanceR[neighbourKey] });
110 | distanceR[neighbourKey] = newWeight;
111 | setR.add({ vertex: neighbourKey, weight: distanceR[neighbourKey] });
112 | parentR[neighbourKey] = { vertex: currentR, weight: revAdjacencyList[currentR].w[i] };
113 | }
114 | }
115 |
116 | procR.add(currentR);
117 |
118 | allEdges.push({
119 | src: [revAdjacencyList[parentR[currentR].vertex].lat, revAdjacencyList[parentR[currentR].vertex].lon],
120 | dest: [revAdjacencyList[currentR].lat, revAdjacencyList[currentR].lon],
121 | weight: parentR[currentR].weight,
122 | });
123 |
124 | if (procF.has(currentR)) {
125 | break;
126 | }
127 | }
128 |
129 | let distance: number = Infinity;
130 | let currBest: string = '';
131 |
132 | procF.forEach((vId) => {
133 | if (distanceF[vId] + distanceR[vId] < distance) {
134 | distance = distanceF[vId] + distanceR[vId];
135 | currBest = vId;
136 | }
137 | });
138 |
139 | procR.forEach((vId) => {
140 | if (distanceF[vId] + distanceR[vId] < distance) {
141 | distance = distanceF[vId] + distanceR[vId];
142 | currBest = vId;
143 | }
144 | });
145 |
146 | let last: string = currBest;
147 |
148 | console.log(distanceF[last], distanceR[last]);
149 |
150 | if (!parentF[last] || !parentR[last]) return { allEdges, edges };
151 |
152 | while (last !== startingKey) {
153 | edges.push({
154 | src: [adjacencyList[parentF[last].vertex].lat, adjacencyList[parentF[last].vertex].lon],
155 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
156 | weight: parentF[last].weight,
157 | });
158 | last = parentF[last].vertex;
159 | }
160 |
161 | edges.reverse();
162 |
163 | last = currBest;
164 |
165 | while (last !== endingKey) {
166 | edges.push({
167 | src: [adjacencyList[parentR[last].vertex].lat, adjacencyList[parentR[last].vertex].lon],
168 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
169 | weight: parentR[last].weight,
170 | });
171 | last = parentR[last].vertex;
172 | }
173 |
174 | return { allEdges, edges };
175 | };
176 |
177 | export default bidirectionalDijkstra;
178 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/bidirectional-a-star-alt.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { Edge, AdjacencyList } from '../utils/interfaces/graph';
3 |
4 | interface VWPair {
5 | vertex: string;
6 | weight: number;
7 | }
8 |
9 | const estimate = (adjacencyList: AdjacencyList, neighbourKey: string, endingKey: string, region: string) => {
10 | const landmarksOfRegion = global.landmarks[region];
11 | let maxEstimate = -999999,
12 | currentEstimate = 0;
13 | Object.keys(landmarksOfRegion).map((key) => {
14 | const landmark = landmarksOfRegion[key];
15 | currentEstimate = Math.abs(landmark.distances[neighbourKey] - landmark.distances[endingKey]);
16 | maxEstimate = Math.max(maxEstimate, currentEstimate);
17 | });
18 | return maxEstimate;
19 | };
20 |
21 | const bidirectionalAStarALT = (
22 | targetVertices: Array,
23 | adjacencyList: AdjacencyList,
24 | revAdjacencyList: AdjacencyList,
25 | region: string
26 | ): { allEdges: Array; edges: Array } => {
27 | const parentF: { [prop: string]: VWPair } = {};
28 | const parentR: { [prop: string]: VWPair } = {};
29 | const distanceF: { [prop: string]: number } = {};
30 | const distanceR: { [prop: string]: number } = {};
31 | const visitedF: { [prop: string]: boolean } = {};
32 | const visitedR: { [prop: string]: boolean } = {};
33 |
34 | const procF: Set = new Set();
35 | const procR: Set = new Set();
36 |
37 | const setF = new SortedSet(
38 | [],
39 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
40 | (a, b) => {
41 | if (a.weight < b.weight) return -1;
42 | if (a.weight > b.weight) return 1;
43 | if (a.vertex !== b.vertex) return -1;
44 | return 0;
45 | }
46 | );
47 |
48 | const setR = new SortedSet(
49 | [],
50 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
51 | (a, b) => {
52 | if (a.weight < b.weight) return -1;
53 | if (a.weight > b.weight) return 1;
54 | if (a.vertex !== b.vertex) return -1;
55 | return 0;
56 | }
57 | );
58 |
59 | const edges: Array = [];
60 | const allEdges: Array = [];
61 |
62 | // Can be cached maybe
63 | // Object.keys(adjacencyList).map((key) => (distanceF[key] = Infinity));
64 | // Object.keys(adjacencyList).map((key) => (distanceR[key] = Infinity));
65 |
66 | let startingKey: string = targetVertices[0];
67 | let endingKey: string = targetVertices[1];
68 |
69 | distanceF[startingKey] = 0;
70 | distanceR[endingKey] = 0;
71 | visitedF[startingKey] = true;
72 | visitedR[endingKey] = true;
73 | parentF[startingKey] = { vertex: startingKey, weight: 0 };
74 | parentR[endingKey] = { vertex: endingKey, weight: 0 };
75 | setF.add({ vertex: startingKey, weight: 0 });
76 | setR.add({ vertex: endingKey, weight: 0 });
77 |
78 | while (setF.length > 0 && setR.length > 0) {
79 | const currentFObj = setF.min();
80 | if (!currentFObj) break;
81 | const currentF = currentFObj.vertex;
82 | setF.delete(currentFObj);
83 |
84 | visitedF[currentF] = true;
85 |
86 | for (let i = 0; i < adjacencyList[currentF].adj.length; i++) {
87 | const neighbourKey: string = adjacencyList[currentF].adj[i].toString();
88 | const currWeight: number = adjacencyList[currentF].w[i];
89 | const newWeight: number = distanceF[currentF] + currWeight;
90 |
91 | if (visitedF[neighbourKey]) continue;
92 |
93 | if (isNaN(distanceF[neighbourKey]) || newWeight < distanceF[neighbourKey]) {
94 | const h = estimate(adjacencyList, neighbourKey, endingKey, region);
95 | // setF.delete({ vertex: neighbourKey, weight: h });
96 | distanceF[neighbourKey] = newWeight;
97 | setF.add({ vertex: neighbourKey, weight: distanceF[neighbourKey] + h });
98 | parentF[neighbourKey] = { vertex: currentF, weight: adjacencyList[currentF].w[i] };
99 | }
100 | }
101 |
102 | procF.add(currentF);
103 |
104 | allEdges.push({
105 | src: [adjacencyList[parentF[currentF].vertex].lat, adjacencyList[parentF[currentF].vertex].lon],
106 | dest: [adjacencyList[currentF].lat, adjacencyList[currentF].lon],
107 | weight: parentF[currentF].weight,
108 | });
109 |
110 | if (procR.has(currentF)) {
111 | break;
112 | }
113 |
114 | const currentRObj = setR.min();
115 | if (!currentRObj) break;
116 | const currentR = currentRObj.vertex;
117 | setR.delete(currentRObj);
118 |
119 | visitedR[currentR] = true;
120 |
121 | for (let i = 0; i < revAdjacencyList[currentR].adj.length; i++) {
122 | const neighbourKey: string = revAdjacencyList[currentR].adj[i].toString();
123 | const currWeight: number = revAdjacencyList[currentR].w[i];
124 | const newWeight: number = distanceR[currentR] + currWeight;
125 |
126 | if (visitedR[neighbourKey]) continue;
127 |
128 | if (isNaN(distanceR[neighbourKey]) || newWeight < distanceR[neighbourKey]) {
129 | const h = estimate(adjacencyList, neighbourKey, endingKey, region);
130 | // setR.delete({ vertex: neighbourKey, weight: h });
131 | distanceR[neighbourKey] = newWeight;
132 | setR.add({ vertex: neighbourKey, weight: distanceR[neighbourKey] + h });
133 | parentR[neighbourKey] = { vertex: currentR, weight: revAdjacencyList[currentR].w[i] };
134 | }
135 | }
136 |
137 | procR.add(currentR);
138 |
139 | allEdges.push({
140 | src: [revAdjacencyList[parentR[currentR].vertex].lat, revAdjacencyList[parentR[currentR].vertex].lon],
141 | dest: [revAdjacencyList[currentR].lat, revAdjacencyList[currentR].lon],
142 | weight: parentR[currentR].weight,
143 | });
144 |
145 | if (procF.has(currentR)) {
146 | break;
147 | }
148 | }
149 | // bidirectionalAStarALT complete
150 |
151 | let distance: number = Infinity;
152 | let currBest: string = '';
153 |
154 | procF.forEach((vId) => {
155 | if (distanceF[vId] + distanceR[vId] < distance) {
156 | distance = distanceF[vId] + distanceR[vId];
157 | currBest = vId;
158 | }
159 | });
160 |
161 | procR.forEach((vId) => {
162 | if (distanceF[vId] + distanceR[vId] < distance) {
163 | distance = distanceF[vId] + distanceR[vId];
164 | currBest = vId;
165 | }
166 | });
167 |
168 | let last: string = currBest;
169 |
170 | console.log('ALT Bi a-star Distance:', distanceF[last] + distanceR[last]);
171 |
172 | if (!parentF[last] || !parentR[last]) return { allEdges, edges };
173 |
174 | while (last !== startingKey) {
175 | edges.push({
176 | src: [adjacencyList[parentF[last].vertex].lat, adjacencyList[parentF[last].vertex].lon],
177 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
178 | weight: parentF[last].weight,
179 | });
180 | last = parentF[last].vertex;
181 | }
182 |
183 | edges.reverse();
184 |
185 | last = currBest;
186 |
187 | while (last !== endingKey) {
188 | edges.push({
189 | src: [adjacencyList[parentR[last].vertex].lat, adjacencyList[parentR[last].vertex].lon],
190 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
191 | weight: parentR[last].weight,
192 | });
193 | last = parentR[last].vertex;
194 | }
195 |
196 | return { allEdges, edges };
197 | };
198 |
199 | export default bidirectionalAStarALT;
200 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/bidirectional-a-star.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { heuristic } from '../utils/functions';
3 | import { Edge, AdjacencyList, LatLon } from '../utils/interfaces/graph';
4 |
5 | interface VWPair {
6 | vertex: string;
7 | weight: number;
8 | }
9 |
10 | const bidirectionalAStar = (
11 | targetVertices: Array,
12 | adjacencyList: AdjacencyList,
13 | revAdjacencyList: AdjacencyList
14 | ): { allEdges: Array; edges: Array } => {
15 | const parentF: { [prop: string]: VWPair } = {};
16 | const parentR: { [prop: string]: VWPair } = {};
17 | const distanceF: { [prop: string]: number } = {};
18 | const distanceR: { [prop: string]: number } = {};
19 | const visitedF: { [prop: string]: boolean } = {};
20 | const visitedR: { [prop: string]: boolean } = {};
21 | const procF: Set = new Set();
22 | const procR: Set = new Set();
23 |
24 | const setF = new SortedSet(
25 | [],
26 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
27 | (a, b) => {
28 | if (a.weight < b.weight) return -1;
29 | if (a.weight > b.weight) return 1;
30 | if (a.vertex !== b.vertex) return -1;
31 | return 0;
32 | }
33 | );
34 |
35 | const setR = new SortedSet(
36 | [],
37 | (a, b) => a.vertex === b.vertex && a.weight === b.weight,
38 | (a, b) => {
39 | if (a.weight < b.weight) return -1;
40 | if (a.weight > b.weight) return 1;
41 | if (a.vertex !== b.vertex) return -1;
42 | return 0;
43 | }
44 | );
45 |
46 | const edges: Array = [];
47 | const allEdges: Array = [];
48 |
49 | // Can be cached maybe
50 | // Object.keys(adjacencyList).map((key) => (distanceF[key] = Infinity));
51 | // Object.keys(adjacencyList).map((key) => (distanceR[key] = Infinity));
52 |
53 | let startingKey: string = targetVertices[0];
54 | let endingKey: string = targetVertices[1];
55 |
56 | distanceF[startingKey] = 0;
57 | distanceR[endingKey] = 0;
58 | visitedF[startingKey] = true;
59 | visitedR[endingKey] = true;
60 | parentF[startingKey] = { vertex: startingKey, weight: 0 };
61 | parentR[endingKey] = { vertex: endingKey, weight: 0 };
62 | const startCoord = { lat: adjacencyList[startingKey].lat, lon: adjacencyList[startingKey].lon };
63 | const endCoord = { lat: adjacencyList[endingKey].lat, lon: adjacencyList[endingKey].lon };
64 | setF.add({ vertex: startingKey, weight: heuristic(startCoord, endCoord) });
65 | setR.add({ vertex: endingKey, weight: heuristic(endCoord, startCoord) });
66 |
67 | while (setF.length > 0 && setR.length > 0) {
68 | const currentFObj = setF.min();
69 | if (!currentFObj) break;
70 | const currentF = currentFObj.vertex;
71 | setF.delete(currentFObj);
72 |
73 | visitedF[currentF] = true;
74 |
75 | for (let i = 0; i < adjacencyList[currentF].adj.length; i++) {
76 | const neighbourKey: string = adjacencyList[currentF].adj[i].toString();
77 | const currWeight: number = adjacencyList[currentF].w[i];
78 | const newWeight: number = distanceF[currentF] + currWeight;
79 |
80 | if (visitedF[neighbourKey]) continue;
81 |
82 | if (isNaN(distanceF[neighbourKey]) || newWeight < distanceF[neighbourKey]) {
83 | const h = heuristic(
84 | { lat: adjacencyList[neighbourKey].lat, lon: adjacencyList[neighbourKey].lon },
85 | { lat: adjacencyList[endingKey].lat, lon: adjacencyList[endingKey].lon }
86 | );
87 | // setF.delete({ vertex: neighbourKey, weight: h });
88 | distanceF[neighbourKey] = newWeight;
89 | setF.add({ vertex: neighbourKey, weight: distanceF[neighbourKey] + h });
90 | parentF[neighbourKey] = { vertex: currentF, weight: adjacencyList[currentF].w[i] };
91 | }
92 | }
93 |
94 | procF.add(currentF);
95 |
96 | allEdges.push({
97 | src: [adjacencyList[parentF[currentF].vertex].lat, adjacencyList[parentF[currentF].vertex].lon],
98 | dest: [adjacencyList[currentF].lat, adjacencyList[currentF].lon],
99 | weight: parentF[currentF].weight,
100 | });
101 |
102 | if (procR.has(currentF)) {
103 | break;
104 | }
105 |
106 | const currentRObj = setR.min();
107 | if (!currentRObj) break;
108 | const currentR = currentRObj.vertex;
109 | setR.delete(currentRObj);
110 |
111 | visitedR[currentR] = true;
112 |
113 | for (let i = 0; i < revAdjacencyList[currentR].adj.length; i++) {
114 | const neighbourKey: string = revAdjacencyList[currentR].adj[i].toString();
115 | const currWeight: number = revAdjacencyList[currentR].w[i];
116 | const newWeight: number = distanceR[currentR] + currWeight;
117 |
118 | if (visitedR[neighbourKey]) continue;
119 |
120 | if (isNaN(distanceR[neighbourKey]) || newWeight < distanceR[neighbourKey]) {
121 | const h = heuristic(
122 | { lat: revAdjacencyList[neighbourKey].lat, lon: revAdjacencyList[neighbourKey].lon },
123 | { lat: revAdjacencyList[startingKey].lat, lon: revAdjacencyList[startingKey].lon }
124 | );
125 | // setR.delete({ vertex: neighbourKey, weight: h });
126 | distanceR[neighbourKey] = newWeight;
127 | setR.add({ vertex: neighbourKey, weight: distanceR[neighbourKey] + h });
128 | parentR[neighbourKey] = { vertex: currentR, weight: revAdjacencyList[currentR].w[i] };
129 | }
130 | }
131 |
132 | procR.add(currentR);
133 |
134 | allEdges.push({
135 | src: [revAdjacencyList[parentR[currentR].vertex].lat, revAdjacencyList[parentR[currentR].vertex].lon],
136 | dest: [revAdjacencyList[currentR].lat, revAdjacencyList[currentR].lon],
137 | weight: parentR[currentR].weight,
138 | });
139 |
140 | if (procF.has(currentR)) {
141 | break;
142 | }
143 | }
144 | // bidirectionalAStar complete
145 |
146 | let distance: number = Infinity;
147 | let currBest: string = '';
148 |
149 | procF.forEach((vId) => {
150 | if (distanceF[vId] + distanceR[vId] < distance) {
151 | distance = distanceF[vId] + distanceR[vId];
152 | currBest = vId;
153 | }
154 | });
155 |
156 | procR.forEach((vId) => {
157 | if (distanceF[vId] + distanceR[vId] < distance) {
158 | distance = distanceF[vId] + distanceR[vId];
159 | currBest = vId;
160 | }
161 | });
162 |
163 | let last: string = currBest;
164 |
165 | console.log('Bi a-star Distance:', distanceF[last] + distanceR[last]);
166 |
167 | if (!parentF[last] || !parentR[last]) return { allEdges, edges };
168 |
169 | while (last !== startingKey) {
170 | edges.push({
171 | src: [adjacencyList[parentF[last].vertex].lat, adjacencyList[parentF[last].vertex].lon],
172 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
173 | weight: parentF[last].weight,
174 | });
175 | last = parentF[last].vertex;
176 | }
177 |
178 | edges.reverse();
179 |
180 | last = currBest;
181 |
182 | while (last !== endingKey) {
183 | edges.push({
184 | src: [adjacencyList[parentR[last].vertex].lat, adjacencyList[parentR[last].vertex].lon],
185 | dest: [adjacencyList[last].lat, adjacencyList[last].lon],
186 | weight: parentR[last].weight,
187 | });
188 | last = parentR[last].vertex;
189 | }
190 |
191 | return { allEdges, edges };
192 | };
193 |
194 | export default bidirectionalAStar;
195 |
--------------------------------------------------------------------------------
/apps/frontend/src/main.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import axios from 'axios';
3 | import { Edge, LatLon } from './utilities/interfaces/graph';
4 | import { Viewport } from './utilities/interfaces/map';
5 | import Map from './components/map';
6 | import LocationSelect from './components/region-select';
7 | import AlgorithmSelect from './components/algorithm-select';
8 | import { Button, Checkbox, Typography } from 'antd';
9 | import ColorPicker from './components/color-picker';
10 | import { AlgorithmsData, Algorithm, Region, RegionsData } from './utilities/interfaces/misc';
11 | import './app.css';
12 | import { calcCrow } from './utilities/functions';
13 | import About from './about';
14 |
15 | const { Title } = Typography;
16 |
17 | const backendUrl = process.env.NODE_ENV === 'production' ? '/api' : 'http://localhost:5000/api';
18 |
19 | let allEdgesTimeouts: Array = [];
20 | let edgesTimeouts: Array = [];
21 | let finishAllEdgeTimeout: NodeJS.Timeout;
22 |
23 | const Main: React.FC = () => {
24 | const [edgeList, setEdgeList] = useState([]);
25 | const [allEdgeList, setAllEdgeList] = useState>([]);
26 |
27 | const [regions, setRegions] = useState({});
28 | const [algorithms, setAlgorithms] = useState({});
29 |
30 | const [currentRegion, setCurrentRegion] = useState(null);
31 | const [currentAlgorithm, setCurrentAlgorithm] = useState(null);
32 |
33 | const [isModalVisible, setIsModalVisible] = useState(true);
34 |
35 | const [viewport] = useState({
36 | lat: 53.53847115373215,
37 | lon: -113.50679397583009,
38 | zoom: 15,
39 | });
40 |
41 | const [targetMarkers, setTargetMarkers] = useState>([]);
42 |
43 | const [showAlgoTrace, setShowAlgoTrace] = useState(false);
44 |
45 | const [traceEdgeColor, setTraceEdgeColor] = useState('#ff003399');
46 | const [mainEdgeColor, setMainEdgeColor] = useState('#0099ff');
47 |
48 | const [estimatedTime, setEstimatedTime] = useState(0.0);
49 |
50 | const [showAnimation, setShowAnimation] = useState(false);
51 |
52 | useEffect(() => {
53 | fetchRegionsData();
54 | fetchAlgorithmsData();
55 | }, []);
56 |
57 | const showModal = () => {
58 | setIsModalVisible(true);
59 | };
60 |
61 | const handleClose = () => {
62 | setIsModalVisible(false);
63 | };
64 | const fetchRegionsData = async () => {
65 | try {
66 | const res = await axios.get(`${backendUrl}/getAllRegions`);
67 | const fetchedRegions = res.data.data;
68 | console.log(fetchedRegions);
69 | setRegions(fetchedRegions);
70 | if (Object.keys(fetchedRegions).length > 0) {
71 | setCurrentRegion(fetchedRegions[Object.keys(fetchedRegions)[0]]);
72 | }
73 | } catch (err: any) {
74 | console.log(err.message);
75 | }
76 | };
77 |
78 | const fetchAlgorithmsData = async () => {
79 | try {
80 | const res = await axios.get(`${backendUrl}/getAllAlgorithms`);
81 | console.log(res.data.data);
82 | setAlgorithms(res.data.data);
83 | } catch (err: any) {
84 | console.log(err.message);
85 | }
86 | };
87 |
88 | useEffect(() => {
89 | let timer: any;
90 | if (estimatedTime > 0) setTimeout(() => setEstimatedTime((time) => (time - 1 <= 1 ? 0 : time - 1)), 1000);
91 | return () => clearTimeout(timer);
92 | }, [estimatedTime]);
93 |
94 | useEffect(() => {
95 | setEdgeList([]);
96 | setAllEdgeList([]);
97 | setTargetMarkers([]);
98 | setShowAlgoTrace(false);
99 | }, [currentRegion]);
100 |
101 | const changeRegion = (region: Region) => {
102 | setCurrentRegion(region);
103 | };
104 |
105 | const changeAlgorithm = (algorithm: Algorithm) => {
106 | setCurrentAlgorithm(algorithm);
107 | };
108 |
109 | const handleAlgoStart = async () => {
110 | try {
111 | setEdgeList([]);
112 | setAllEdgeList([]);
113 | clearTimeout(finishAllEdgeTimeout);
114 | allEdgesTimeouts.map((item) => clearTimeout(item));
115 | edgesTimeouts.map((item) => clearTimeout(item));
116 | if (!currentAlgorithm || !currentRegion) throw new Error('Select an algorithm');
117 | const body = {
118 | region: currentRegion.key,
119 | algorithm: currentAlgorithm.key,
120 | pointA: targetMarkers[0],
121 | pointB: targetMarkers[1],
122 | };
123 | const res = await axios.post(`${backendUrl}/execAlgorithm`, body, {
124 | headers: { contentType: 'application/json' },
125 | });
126 | const { allEdges, edges }: { allEdges: Array; edges: Array } = res.data.data;
127 | console.log('allEdges size:', allEdges.length, 'edges size:', edges.length);
128 | renderAlgoOutput(targetMarkers[0], targetMarkers[1], allEdges, edges);
129 | } catch (err: any) {
130 | alert(err.message);
131 | }
132 | };
133 |
134 | const renderAlgoOutput = (start: LatLon, end: LatLon, allEdges: Array, edges: Array) => {
135 | let timeToFinishAllEdges = 0;
136 |
137 | const speed = 1920 / 10; //check css
138 |
139 | if (showAlgoTrace) {
140 | if (!showAnimation) {
141 | setAllEdgeList(allEdges);
142 | } else {
143 | for (let i = 1; i <= allEdges.length; i++) {
144 | const edge = allEdges[i - 1];
145 | const distance: number = calcCrow(edge.src[0], edge.src[1], edge.dest[0], edge.dest[1]); // km
146 | const time = (distance / speed) * 1000;
147 | timeToFinishAllEdges += time;
148 | allEdgesTimeouts.push(setTimeout(() => setAllEdgeList((edges) => [...edges, edge]), i * 100 + time));
149 | }
150 | }
151 | }
152 |
153 | timeToFinishAllEdges += allEdges.length * 100 + 500;
154 | console.log('time req', timeToFinishAllEdges);
155 | setEstimatedTime(Math.round(timeToFinishAllEdges / 1000));
156 |
157 | if (edges.length === 0) return console.log('No paths found');
158 |
159 | if (!showAnimation) {
160 | setEdgeList(edges);
161 | } else {
162 | finishAllEdgeTimeout = setTimeout(() => {
163 | for (let i = 1; i <= edges.length; i++) {
164 | const edge = edges[i - 1];
165 | const distance: number = calcCrow(edge.src[0], edge.src[1], edge.dest[0], edge.dest[1]); // km
166 | const time = (distance / speed) * 1000;
167 | edgesTimeouts.push(setTimeout(() => setEdgeList((edges) => [...edges, edge]), i * 50 + time));
168 | }
169 | }, timeToFinishAllEdges);
170 | }
171 | };
172 |
173 | return (
174 |
175 |
176 |
177 |
178 |
179 |
handleAlgoStart()}>Start visualization
180 |
setShowAlgoTrace(e.target.checked)}>
181 | Show visualization?
182 |
183 |
setShowAnimation(e.target.checked)}>
184 | Animate?
185 |
186 |
187 |
188 |
About This Project
189 | {showAlgoTrace && showAnimation &&
Estimated time: {estimatedTime} }
190 |
191 | {currentRegion && (
192 | //
193 |
203 | )}
204 |
205 | );
206 | };
207 |
208 | export default Main;
209 |
--------------------------------------------------------------------------------
/apps/frontend/src/components/map/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState, useRef, memo } from 'react';
2 | import { LatLngExpression, LeafletMouseEvent, Map as MapClass } from 'leaflet';
3 | import { MapContainer, Marker, Polyline, Popup, Rectangle, TileLayer, LayersControl, ZoomControl } from 'react-leaflet';
4 | import ReactLeafletGoogleLayer from 'react-leaflet-google-layer';
5 | import { Edge, LatLon } from '../../utilities/interfaces/graph';
6 | import { Map as MapInterface } from '../../utilities/interfaces/map';
7 | // import dfs from '../../algorithms/dfs';
8 | import axios from 'axios';
9 |
10 | const mapHeight = '100%';
11 | const localBackendUrl = 'http://localhost:5000/api';
12 |
13 | const Map: React.FC = (props) => {
14 | const { allEdgeList, edgeList, viewport, currentRegion, traceEdgeColor, mainEdgeColor, targetMarkers, setTargetMarkers } = props;
15 |
16 | const mapRef = useRef(null);
17 |
18 | const [landmarks, setLandmarks] = useState<{ id: string; lat: number; lon: number }[]>([]);
19 |
20 | useEffect(() => {
21 | // fetchLandmarks();
22 | if (!currentRegion) return;
23 | const centerLat = currentRegion.boundary[3] + (currentRegion.boundary[1] - currentRegion.boundary[3]) / 2;
24 | const centerLon = currentRegion.boundary[2] + (currentRegion.boundary[0] - currentRegion.boundary[2]) / 2;
25 | handlePositionChange(centerLat, centerLon);
26 | }, [currentRegion]);
27 |
28 | const handlePositionChange = (lat: number, lon: number) => {
29 | setTimeout(() => {
30 | mapRef.current?.flyTo([lat, lon]);
31 | }, 500);
32 | };
33 |
34 | // const fetchLandmarks = async () => {
35 | // try {
36 | // const res = await axios.get(localBackendUrl + '/getLandmarks/' + currentRegion.key);
37 | // console.log(res.data.data.landmarks);
38 | // setLandmarks(res.data.data.landmarks);
39 | // } catch (err) {
40 | // console.log(err);
41 | // }
42 | // };
43 |
44 | const deleteLandmark = async (id: string) => {
45 | try {
46 | const res = await axios.post(
47 | localBackendUrl + '/deleteLandmark',
48 | { id, region: currentRegion.key },
49 | {
50 | headers: {
51 | contentType: 'application/json',
52 | },
53 | }
54 | );
55 | console.log(res.data);
56 | setLandmarks((landmarks) => landmarks.filter((landmark) => landmark.id !== id));
57 | } catch (err) {
58 | console.log(err);
59 | }
60 | };
61 |
62 | const displayTargetMarkers = targetMarkers.map((coords: LatLon, idx: number) => {
63 | return (
64 |
65 |
66 | {`Lat: ${coords.lat}, Lon: ${coords.lon}`}
67 |
68 | Index: {idx}
69 |
70 | ID: {idx}
71 |
72 |
73 | );
74 | });
75 |
76 | const displayLandmarks = landmarks.map((landmark, idx) => {
77 | return (
78 | deleteLandmark(landmark.id),
83 | }}
84 | >
85 |
86 | {`Lat: ${landmark.lat}, Lon: ${landmark.lon}`}
87 |
88 | Index: {idx}
89 |
90 | ID: {landmark.id}
91 |
92 |
93 | );
94 | });
95 |
96 | // const displayTestMarkers = Object.keys(testMarkers).map((id) => {
97 | // // @ts-ignore
98 | // const vertex = testMarkers[id];
99 | // return +id >= 6 ? null : (
100 | //
101 | //
102 | // {`Lat: ${vertex.lat}, Lon: ${vertex.lon}`}
103 | //
104 | // ID: {id}
105 | //
106 | //
107 | // );
108 | // });
109 |
110 | const addTargetMarker = (lat: number, lon: number) => {
111 | setTargetMarkers((markers) => (markers.length < 2 ? [...markers, { lat, lon }] : [{ lat, lon }]));
112 | };
113 |
114 | const { BaseLayer } = LayersControl;
115 |
116 | const handleMapOnCreation = (mapInstance: MapClass) => {
117 | mapRef.current = mapInstance;
118 | mapRef.current?.addEventListener('click', (e: LeafletMouseEvent) => {
119 | console.log(`lat: ${e.latlng.lat}, lon: ${e.latlng.lng}`);
120 | addTargetMarker(e.latlng.lat, e.latlng.lng);
121 | });
122 | };
123 |
124 | return (
125 | handleMapOnCreation(mapInstance)}
128 | center={[viewport.lat, viewport.lon]}
129 | zoom={viewport.zoom}
130 | style={{ height: mapHeight, width: '100%' }}
131 | zoomControl={false}
132 | >
133 |
134 |
135 |
141 |
142 |
143 |
149 |
150 |
151 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
170 | {/* {markers} */}
171 | {/* {displayTestMarkers} */}
172 | {displayTargetMarkers}
173 | {displayLandmarks}
174 | { }
175 | { }
176 |
177 | );
178 | };
179 |
180 | export default Map;
181 |
182 | const AllEdgeList = memo((props: any) => {
183 | const { allEdgeList, traceEdgeColor } = props;
184 | return allEdgeList.map((edge: Edge, idx: number) => (
185 |
192 | ));
193 | });
194 |
195 | const MainEdgeList = memo((props: any) => {
196 | const { mainEdgeList, mainEdgeColor } = props;
197 | return mainEdgeList.map((edge: Edge, idx: number) => (
198 |
205 |
206 | {`Index: ${idx}`}
207 |
208 | {`Src: Lat: ${edge.src[0]}, Lon: ${edge.src[1]}`}
209 |
210 | {`Dest: Lat: ${edge.dest[0]}, Lon: ${edge.dest[1]}`}
211 |
212 | {`Weight: ${edge.weight}`}
213 |
214 |
215 | ));
216 | });
217 |
--------------------------------------------------------------------------------
/apps/backend/src/algorithms/algorithm-handler.ts:
--------------------------------------------------------------------------------
1 | import { latLonIndexTree } from '../data-structures/lat-lon-index-tree';
2 | import { algorithmsData } from '../data/algorithms';
3 | import { regionsData } from '../data/regions';
4 | import { AdjacencyList, Edge, LatLon, Vertex } from '../utils/interfaces/graph';
5 | import bfs from './bfs';
6 | import dijkstra from './dijkstra';
7 | import bidirectionalDijkstra from './bidirectional-dijkstra';
8 | import aStar from './a-star';
9 | import bidirectionalAStar from './bidirectional-a-star';
10 | import bidirectionalAStarALT from './bidirectional-a-star-alt';
11 | import fs from 'fs';
12 |
13 | const currentTime = new Date().toLocaleTimeString().split(' ').join('-').toLowerCase();
14 | if (!fs.existsSync('./logs')) {
15 | fs.mkdirSync('./logs');
16 | }
17 |
18 | if (fs.existsSync('./logs/info.log')) {
19 | fs.unlinkSync('./logs/info.log');
20 | }
21 | const stream = fs.createWriteStream(`./logs/info.log`, { flags: 'a' });
22 | const streamBkp = fs.createWriteStream(`./logs/info-${currentTime}.log`, { flags: 'a' });
23 |
24 | export const handleAlgorithmExec = (
25 | region: string,
26 | algorithm: string,
27 | pointA: LatLon,
28 | pointB: LatLon
29 | ): Error | { edges: Array; allEdges: Array } => {
30 | if (!algorithmsData[algorithm]) return new Error('Invalid algorithm');
31 | if (!regionsData[region]) return new Error('Invalid region');
32 |
33 | const fullAdjacencyList = global.fullAdjacencyList;
34 | const revFullAdjacencyList = global.revFullAdjacencyList;
35 | const adjacencyList: AdjacencyList = fullAdjacencyList[region];
36 | const revAdjacencyList: AdjacencyList = revFullAdjacencyList[region];
37 | // Get closest vertices of the selected points
38 | const { key: keyA, vertex: vertexA, closestPoints: closestPointsA } = findNeighboursOfPoints(pointA);
39 | const { key: keyB, vertex: vertexB, closestPoints: closestPointsB } = findNeighboursOfPoints(pointB);
40 |
41 | // Add them to adj list
42 | addPointToAdjacencyList(adjacencyList, keyA, vertexA, closestPointsA);
43 | addPointToAdjacencyList(adjacencyList, keyB, vertexB, closestPointsB);
44 |
45 | const landmarksOfRegion = global.landmarks[region];
46 | Object.keys(landmarksOfRegion).map((landmarkId) => {
47 | const landmark = landmarksOfRegion[landmarkId];
48 | let minDistA = Infinity;
49 | let minDistB = Infinity;
50 | vertexA.adj.map((id) => {
51 | minDistA = Math.min(minDistA, landmark.distances[id]);
52 | });
53 | vertexB.adj.map((id) => {
54 | minDistB = Math.min(minDistB, landmark.distances[id]);
55 | });
56 | landmark.distances[keyA] = minDistA;
57 | landmark.distances[keyB] = minDistB;
58 | });
59 |
60 | // Run algorithm
61 | const start = new Date().getTime();
62 | const { allEdges, edges } = filterAndExecAlgorithm(
63 | algorithm,
64 | [keyA, keyB],
65 | [closestPointsA[0].toString(), closestPointsB[0].toString()],
66 | adjacencyList,
67 | revAdjacencyList,
68 | region
69 | );
70 |
71 | const end = new Date().getTime();
72 | const execTime = end - start;
73 | console.info('Execution time: %dms', execTime);
74 |
75 | // Logging execution times
76 | const execTimeLogObj = {
77 | initialLandmark: global.initialLandmark,
78 | landmarksCount: global.landmarksCount,
79 | algorithm,
80 | execTime,
81 | pointA,
82 | pointB,
83 | };
84 | stream.write(JSON.stringify(execTimeLogObj) + '\n', (err) => {
85 | if (err) {
86 | console.error(err.message);
87 | }
88 | });
89 | streamBkp.write(JSON.stringify(execTimeLogObj) + '\n', (err) => {
90 | if (err) {
91 | console.error(err.message);
92 | }
93 | });
94 |
95 | // Remove the vertices from list
96 | deletePointFromAdjacencyList(adjacencyList, keyA, closestPointsA);
97 | deletePointFromAdjacencyList(adjacencyList, keyB, closestPointsB);
98 |
99 | Object.keys(landmarksOfRegion).map((landmarkId) => {
100 | const landmark = landmarksOfRegion[landmarkId];
101 | delete landmark.distances[keyA];
102 | delete landmark.distances[keyB];
103 | });
104 |
105 | // return the resultant allEdges and edges
106 | return { edges, allEdges };
107 | };
108 |
109 | const filterAndExecAlgorithm = (
110 | algorithm: string,
111 | targetVertices: Array,
112 | closestVertices: Array,
113 | adjacencyList: AdjacencyList,
114 | revAdjacencyList: AdjacencyList,
115 | region: string
116 | ): { allEdges: Array; edges: Array } => {
117 | if (algorithm === 'bfs') return bfs(targetVertices, adjacencyList);
118 | if (algorithm === 'dijkstra') return dijkstra(targetVertices, adjacencyList);
119 | if (algorithm === 'contractionHierarchy') {
120 | const { allEdges, edges } = global.contractionHierarchy.query(+closestVertices[0], +closestVertices[1]);
121 | edges.push({
122 | src: [adjacencyList[targetVertices[0]].lat, adjacencyList[targetVertices[0]].lon],
123 | dest: [adjacencyList[closestVertices[0]].lat, adjacencyList[closestVertices[0]].lon],
124 | weight: 0,
125 | });
126 | edges.push({
127 | src: [adjacencyList[targetVertices[1]].lat, adjacencyList[targetVertices[1]].lon],
128 | dest: [adjacencyList[closestVertices[1]].lat, adjacencyList[closestVertices[1]].lon],
129 | weight: 0,
130 | });
131 | return { allEdges, edges };
132 | }
133 | if (algorithm === 'bidirectionalDijkstra') return bidirectionalDijkstra(targetVertices, adjacencyList, revAdjacencyList);
134 | if (algorithm === 'aStar') return aStar(targetVertices, adjacencyList);
135 | if (algorithm === 'bidirectionalAStar') return bidirectionalAStar(targetVertices, adjacencyList, revAdjacencyList);
136 | if (algorithm === 'bidirectionalAStarALT') return bidirectionalAStarALT(targetVertices, adjacencyList, revAdjacencyList, region);
137 | return { allEdges: [], edges: [] };
138 | };
139 |
140 | export const addPointToAdjacencyList = (
141 | adjacencyList: AdjacencyList,
142 | newKey: string,
143 | newVertex: Vertex,
144 | closestPoints: Array
145 | ) => {
146 | adjacencyList[newKey] = newVertex;
147 | closestPoints.map((key) => {
148 | const currentVertex: Vertex = adjacencyList[key];
149 | adjacencyList[key] = { ...currentVertex, adj: [...currentVertex.adj, +newKey], w: [...currentVertex.w, 0] };
150 | });
151 | };
152 |
153 | export const deletePointFromAdjacencyList = (adjacencyList: AdjacencyList, newKey: string, closestPoints: Array) => {
154 | delete adjacencyList[newKey];
155 | closestPoints.map((key) => {
156 | const vertex: Vertex = adjacencyList[key];
157 | const idx = vertex.adj.indexOf(+newKey);
158 | if (idx >= 0) {
159 | vertex.adj.splice(idx, 1);
160 | vertex.w.splice(idx, 1);
161 | }
162 | });
163 | };
164 |
165 | export const findNeighboursOfPoints = (
166 | point: LatLon,
167 | areaToScan: string = 'normal'
168 | ): { key: string; vertex: Vertex; closestPoints: Array } => {
169 | let closeLats: Array = [];
170 | let closeLons: Array = [];
171 | let closeFinal: Array = [];
172 | let incVal = 0.0001;
173 | let i = incVal;
174 |
175 | while (closeFinal.length === 0 && i <= (areaToScan === 'normal' ? 0.01 : 0.1)) {
176 | // Will contain keys from adjacency list that have close latitudes
177 | closeLats = [
178 | ...(latLonIndexTree.latitudes[+point.lat.toFixed(3)] || []),
179 | ...(latLonIndexTree.latitudes[(+point.lat.toFixed(3) - i).toFixed(3)] || []),
180 | ...(latLonIndexTree.latitudes[(+point.lat.toFixed(3) + i).toFixed(3)] || []),
181 | ];
182 |
183 | // Will contain keys from adjacency list that have close longitudes
184 | closeLons = [
185 | ...(latLonIndexTree.longitudes[+point.lon.toFixed(3)] || []),
186 | ...(latLonIndexTree.longitudes[(+point.lon.toFixed(3) - i).toFixed(3)] || []),
187 | ...(latLonIndexTree.longitudes[(+point.lon.toFixed(3) + i).toFixed(3)] || []),
188 | ];
189 |
190 | closeFinal = closeLats.filter((key) => closeLons.includes(key)).map((x) => +x);
191 | closeFinal = [...new Set(closeFinal)];
192 | i += incVal;
193 | }
194 |
195 | const newKey: string = `${Date.now()}${Math.floor(Math.random() * (1000 - 100 + 1)) + 100}`;
196 | const newVertex: Vertex = {
197 | lat: point.lat,
198 | lon: point.lon,
199 | adj: closeFinal,
200 | w: new Array(closeFinal.length).fill(0),
201 | };
202 |
203 | return { key: newKey, vertex: newVertex, closestPoints: closeFinal };
204 | };
205 |
--------------------------------------------------------------------------------
/apps/backend/src/data-structures/contraction-hierarchy.ts:
--------------------------------------------------------------------------------
1 | import SortedSet from 'collections/sorted-set';
2 | import { regionsData } from '../data/regions';
3 | import { Edge } from '../utils/interfaces/graph';
4 |
5 | interface Node {
6 | first: number;
7 | second: number;
8 | }
9 |
10 | const REGION = Object.keys(regionsData).find((key) => regionsData[key].contractionAvailable === true) || '';
11 |
12 | const pairToStr = (a: any, b: any) => {
13 | return a.toString() + '#' + b.toString();
14 | };
15 |
16 | const backFromStr = (s: string) => {
17 | const [a, b] = s.split('#');
18 | return { first: +a, second: +b };
19 | };
20 |
21 | class ContractionHierarchy {
22 | private n = 0;
23 | private m = 0;
24 | private s = 0;
25 | private t = 0;
26 | private order = 0;
27 | private G: Array>> = []; // G[0] = Forward graph, G[1] = Reversed Graph
28 | private G_CH: Array>> = [];
29 | private dist: Array> = [];
30 | private parent: Array<{ [key: number]: number }> = [];
31 | private proc: Array> = [];
32 | private OparentMap: { [key: string]: number } = {};
33 | private dp: { [key: string]: Array<[number, number]> } = {};
34 | private edges: Edge[] = [];
35 | private allEdges: Edge[] = [];
36 | private pq: any[] = []; // SortedSet
37 | private imp_pq: any = new SortedSet(
38 | [],
39 | (a, b) => a.second === b.second && a.first === b.first,
40 | (a, b) => {
41 | if (a.first < b.first) return -1;
42 | if (a.first > b.first) return 1;
43 | if (a.second !== b.second) return -1;
44 | return 0;
45 | }
46 | ); // SortedSet
47 | private contracted: Array = [];
48 | private imp: Array = [];
49 | private level: Array = [];
50 | private contr_neighbours: Array = [];
51 |
52 | private read() {
53 | console.log('Reading...');
54 | const adjacencyList = global.fullAdjacencyList[REGION];
55 | const adjacencyListArr = Object.keys(adjacencyList);
56 | this.n = adjacencyListArr.length;
57 | this.G.push([]);
58 | this.G.push([]);
59 | for (let i = 0; i < 2; i++) {
60 | for (let j = 0; j < this.n + 1; j++) {
61 | this.G[i].push([]);
62 | }
63 | }
64 | let u = 0;
65 | let v = 0;
66 | let w = 0;
67 | Object.keys(adjacencyList).map((vertexId) => {
68 | const vertex = adjacencyList[vertexId];
69 | vertex.adj.map((neighbourId, idx) => {
70 | u = +vertexId;
71 | v = neighbourId;
72 | w = vertex.w[idx];
73 | if (u !== v) {
74 | this.connect(this.G[0][u], v, w);
75 | this.connect(this.G[1][v], u, w);
76 | }
77 | });
78 | });
79 | }
80 |
81 | private connect(E: Node[], v: number, w: number) {
82 | for (let p of E) {
83 | if (p.first === v) {
84 | p.second = Math.min(p.second, w);
85 | return;
86 | }
87 | }
88 | const newVal: Node = { first: v, second: w } as Node;
89 | E.push(newVal);
90 | }
91 |
92 | private preprocess() {
93 | this.setOrder();
94 | this.build_G_CH();
95 | }
96 |
97 | private setOrder() {
98 | for (let i = 0; i <= this.n; i++) {
99 | this.contracted.push(false);
100 | this.imp.push(0);
101 | this.level.push(0);
102 | this.contr_neighbours.push(0);
103 | }
104 | for (let i = 1; i <= this.n; i++) {
105 | this.imp_pq.add({ first: -this.n, second: i });
106 | }
107 | let currentNode = 0;
108 | let newImp = 0;
109 | this.order = 1;
110 | while (this.imp_pq.length > 0) {
111 | let currNodeObj = this.imp_pq.min();
112 | if (!currNodeObj) break;
113 | currentNode = currNodeObj.second;
114 | this.imp_pq.delete(currNodeObj);
115 | newImp = this.getImportance(currentNode);
116 | currNodeObj = this.imp_pq.min();
117 | if (!currNodeObj || newImp - currNodeObj.first <= 10) {
118 | this.imp[currentNode] = this.order++;
119 | this.contracted[currentNode] = true;
120 | this.contractNode(currentNode);
121 | } else {
122 | this.imp_pq.add({ first: newImp, second: currentNode });
123 | }
124 | }
125 | }
126 |
127 | private getImportance(x: number): number {
128 | let u = 0;
129 | let v = 0;
130 | let shortcuts = 0;
131 | let inOut = 0;
132 | for (let i = 0; i < 2; i++) {
133 | for (let p of this.G[i][x]) {
134 | if (!this.contracted[p.first]) {
135 | inOut += 1;
136 | }
137 | }
138 | }
139 | for (let p1 of this.G[1][x]) {
140 | for (let p2 of this.G[0][x]) {
141 | u = p1.first;
142 | v = p2.first;
143 | if (!this.contracted[u] && !this.contracted[v]) {
144 | shortcuts += 1;
145 | }
146 | }
147 | }
148 | const edgeDiff = shortcuts - inOut;
149 | return edgeDiff + 2 * this.contr_neighbours[x] + this.level[x];
150 | }
151 |
152 | private contractNode(x: number) {
153 | let u = 0;
154 | let w = 0;
155 | let mx = this.getMaxEdge(x);
156 | // outEdges => pair => stringify => firstVal#secondVal
157 | const outEdges: Set = new Set();
158 | for (let p of this.G[0][x]) {
159 | if (!this.contracted[p.first]) {
160 | outEdges.add(pairToStr(p.first, p.second));
161 | }
162 | }
163 | for (let p of this.G[1][x]) {
164 | u = p.first;
165 | if (!this.contracted[u]) {
166 | w = p.second;
167 | // x -> first, u -> second
168 | this.checkWitness(u, x, w, mx, outEdges, false);
169 | }
170 | }
171 | for (let i = 0; i < 2; i++) {
172 | for (let p of this.G[i][x]) {
173 | this.contr_neighbours[p.first] += 1;
174 | this.level[p.first] = Math.max(this.level[p.first], this.level[x] + 1);
175 | }
176 | }
177 | }
178 |
179 | private getMaxEdge(x: number): number {
180 | let ret = 0;
181 | for (let p1 of this.G[1][x]) {
182 | for (let p2 of this.G[0][x]) {
183 | if (p1.first !== p2.first && !this.contracted[p1.first] && !this.contracted[p2.first]) {
184 | ret = Math.max(ret, p1.second + p2.second);
185 | }
186 | }
187 | }
188 | return ret;
189 | }
190 |
191 | private checkWitness(u: number, x: number, w: number, mx: number, outEdges: Set, type: boolean): number {
192 | let a = 0;
193 | let b = 0;
194 | let currDist = 0;
195 | let newDist = 0;
196 | const D_pq: any = new SortedSet(
197 | [],
198 | (a, b) => a.second === b.second && a.first === b.first,
199 | (a, b) => {
200 | if (a.first < b.first) return -1;
201 | if (a.first > b.first) return 1;
202 | if (a.second !== b.second) return -1;
203 | return 0;
204 | }
205 | ); // SortedSet
206 | const D_dist: { [key: number]: number } = {};
207 | D_pq.add({ first: 0, second: u });
208 | D_dist[u] = 0;
209 | let iter = (250 * (this.n - this.order)) / this.n;
210 | while (D_pq.length > 0 && iter > 0) {
211 | iter -= 1;
212 | let currentDistObj = D_pq.min();
213 | if (!currentDistObj) break;
214 | currDist = currentDistObj.first;
215 | a = currentDistObj.second;
216 | D_pq.delete(currentDistObj);
217 | if (currDist <= D_dist[a]) {
218 | for (let p of this.G[0][a]) {
219 | newDist = p.second + currDist;
220 | b = p.first;
221 | // this.OparentMap[`${x}@${b}`] = a;
222 | // this.OparentMap[`${b}@${x}`] = a;
223 | if (b !== x && !this.contracted[b]) {
224 | if (isNaN(D_dist[b]) || D_dist[b] > newDist) {
225 | if (isNaN(D_dist[b]) || D_dist[b] < mx) {
226 | D_dist[b] = newDist;
227 | D_pq.add({ first: newDist, second: b });
228 | // this.OparentMap[`${x}@${b}`] = a;
229 | // this.OparentMap[`${b}@${x}`] = a;
230 | }
231 | }
232 | }
233 | }
234 | }
235 | }
236 | let v = 0;
237 | let ret = 0;
238 | let new_w = 0;
239 | for (let pTmp of outEdges) {
240 | let p = backFromStr(pTmp);
241 | v = p.first;
242 | new_w = w + p.second;
243 | if (isNaN(D_dist[v]) || D_dist[v] > new_w) {
244 | ret += 1;
245 | if (!type && u !== v) {
246 | this.connect(this.G[0][u], v, new_w);
247 | this.connect(this.G[1][v], u, new_w);
248 | this.OparentMap[`${u}@${v}`] = x;
249 | this.OparentMap[`${v}@${u}`] = x;
250 | }
251 | }
252 | }
253 | return ret;
254 | }
255 |
256 | private build_G_CH() {
257 | this.G_CH.push([]);
258 | this.G_CH.push([]);
259 | for (let i = 0; i < 2; i++) {
260 | for (let j = 0; j < this.n + 1; j++) {
261 | this.G_CH[i].push([]);
262 | }
263 | }
264 | let v = 0;
265 | let w = 0;
266 | for (let u = 1; u <= this.n; u++) {
267 | for (let p of this.G[0][u]) {
268 | v = p.first;
269 | w = p.second;
270 | if (this.imp[v] > this.imp[u]) {
271 | this.G_CH[0][u].push({ first: v, second: w });
272 | } else {
273 | this.G_CH[1][v].push({ first: u, second: w });
274 | }
275 | }
276 | }
277 | }
278 |
279 | private getDistance(): number {
280 | this.dist[0][this.s] = 0;
281 | this.dist[1][this.t] = 0;
282 | this.parent[0][this.s] = this.s;
283 | this.parent[1][this.t] = this.t;
284 | let SP = Infinity;
285 | this.pq[0].add({ first: 0, second: this.s });
286 | this.pq[1].add({ first: 0, second: this.t });
287 | let front: Node | undefined = { first: 0, second: 0 };
288 | let currentNode = 0;
289 | let currentDist = 0;
290 | while (this.pq[0].length > 0 || this.pq[1].length > 0) {
291 | if (this.pq[0].length > 0) {
292 | front = this.pq[0].min();
293 | if (!front) break;
294 | this.pq[0].delete(front);
295 | currentNode = front.second;
296 | currentDist = front.first;
297 | if (SP >= currentDist) {
298 | this.relaxNodeEdges(currentNode, 0);
299 | }
300 | this.proc[0].add(currentNode);
301 | if (this.proc[1].has(currentNode) && this.dist[0][currentNode] + this.dist[1][currentNode] < SP) {
302 | SP = this.dist[0][currentNode] + this.dist[1][currentNode];
303 | }
304 | // SP = Math.min(SP, this.dist[0][currentNode] + this.dist[1][currentNode]);
305 | // this.allEdges.push({
306 | // src: [adjL[this.parent[0][currentNode]].lat, adjL[this.parent[0][currentNode]].lon],
307 | // dest: [adjL[currentNode].lat, adjL[currentNode].lon],
308 | // weight: 0,
309 | // });
310 | }
311 | if (this.pq[1].length > 0) {
312 | let front = this.pq[1].min();
313 | if (!front) break;
314 | this.pq[1].delete(front);
315 | currentNode = front.second;
316 | currentDist = front.first;
317 | if (SP >= currentDist) {
318 | this.relaxNodeEdges(currentNode, 1);
319 | }
320 | this.proc[1].add(currentNode);
321 | if (this.proc[0].has(currentDist) && this.dist[0][currentNode] + this.dist[1][currentNode] < SP) {
322 | SP = this.dist[0][currentNode] + this.dist[1][currentNode];
323 | }
324 | // SP = Math.min(SP, this.dist[0][currentNode] + this.dist[1][currentNode]);
325 | // this.allEdges.push({
326 | // src: [adjL[this.parent[1][currentNode]].lat, adjL[this.parent[1][currentNode]].lon],
327 | // dest: [adjL[currentNode].lat, adjL[currentNode].lon],
328 | // weight: 0,
329 | // });
330 | }
331 | }
332 | if (SP === Infinity) {
333 | return -1;
334 | }
335 | return SP;
336 | }
337 |
338 | private relaxNodeEdges(u: number, g: number) {
339 | let v = 0;
340 | let w = 0;
341 | for (let p of this.G_CH[g][u]) {
342 | v = p.first;
343 | w = p.second;
344 | if (this.dist[g][v] > this.dist[g][u] + w) {
345 | this.dist[g][v] = this.dist[g][u] + w;
346 | this.pq[g].add({ first: this.dist[g][v], second: v });
347 | // this.addAllEdge(u, v, 'allEdges');
348 | this.parent[g][v] = u;
349 | }
350 | }
351 | }
352 |
353 | private addAllEdge(u: number, v: number, type: string): Array<[number, number]> {
354 | const s = `${u}@${v}`;
355 | const adjL = global.fullAdjacencyList[REGION];
356 | const x = this.OparentMap[s];
357 |
358 | // Base case
359 | if (!x) {
360 | if (type === 'edges') {
361 | this.edges.push({
362 | src: [adjL[u].lat, adjL[u].lon],
363 | dest: [adjL[v].lat, adjL[v].lon],
364 | weight: 0,
365 | });
366 | } else if (type === 'allEdges') {
367 | this.allEdges.push({
368 | src: [adjL[u].lat, adjL[u].lon],
369 | dest: [adjL[v].lat, adjL[v].lon],
370 | weight: 0,
371 | });
372 | }
373 | return [[u, v]];
374 | }
375 |
376 | // Memoized case
377 | if (this.dp[s]) {
378 | let arr = this.dp[s];
379 | arr.map((el) => {
380 | if (type === 'edges') {
381 | this.edges.push({
382 | src: [adjL[el[0]].lat, adjL[el[0]].lon],
383 | dest: [adjL[el[1]].lat, adjL[el[1]].lon],
384 | weight: 0,
385 | });
386 | } else if (type === 'allEdges') {
387 | this.allEdges.push({
388 | src: [adjL[el[0]].lat, adjL[el[0]].lon],
389 | dest: [adjL[el[1]].lat, adjL[el[1]].lon],
390 | weight: 0,
391 | });
392 | }
393 | });
394 | return arr;
395 | }
396 |
397 | let arr: Array<[number, number]> = [];
398 | arr = [...arr, ...this.addAllEdge(u, x, type)];
399 | arr = [...arr, ...this.addAllEdge(x, v, type)];
400 | this.dp[s] = arr;
401 | return arr;
402 | }
403 |
404 | constructor() {
405 | console.log('Preprocessing...');
406 | this.read();
407 | this.preprocess();
408 | console.log('Preprocessing done!');
409 | let cnt = 0;
410 | for (let i of this.contracted) {
411 | if (i) cnt += 1;
412 | }
413 | console.log('Contracted', cnt, 'nodes');
414 | }
415 |
416 | public query(_s: number, _t: number): { allEdges: Edge[]; edges: Edge[] } {
417 | this.s = _s;
418 | this.t = _t;
419 | this.allEdges = [];
420 | this.edges = [];
421 | if (this.proc.length === 2 && (this.proc[0].size > 0 || this.proc[1].size > 0)) {
422 | this.proc[0].forEach((id) => (this.dist[0][id] = Infinity));
423 | this.proc[1].forEach((id) => (this.dist[1][id] = Infinity));
424 | } else {
425 | this.dist = [];
426 | this.dist.push([]);
427 | this.dist.push([]);
428 | for (let i = 0; i < 2; i++) {
429 | for (let j = 0; j < this.n + 1; j++) {
430 | this.dist[i].push(Infinity);
431 | }
432 | }
433 | }
434 | this.parent = [];
435 | this.parent.push({});
436 | this.parent.push({});
437 | this.proc = [];
438 | this.proc.push(new Set());
439 | this.proc.push(new Set());
440 | this.pq = [];
441 | for (let i = 0; i < 2; i++) {
442 | this.pq.push(
443 | new SortedSet(
444 | [],
445 | (a, b) => a.second === b.second && a.first === b.first,
446 | (a, b) => {
447 | if (a.first < b.first) return -1;
448 | if (a.first > b.first) return 1;
449 | if (a.second !== b.second) return -1;
450 | return 0;
451 | }
452 | )
453 | );
454 | }
455 | const distance = this.getDistance();
456 | console.log('CH query distance:', distance);
457 | let distEstimate: number = Infinity;
458 | let currBest: number = 0;
459 |
460 | this.proc[0].forEach((vId) => {
461 | if (this.dist[0][vId] + this.dist[1][vId] < distEstimate) {
462 | distEstimate = this.dist[0][vId] + this.dist[1][vId];
463 | currBest = vId;
464 | }
465 | });
466 |
467 | this.proc[1].forEach((vId) => {
468 | if (this.dist[0][vId] + this.dist[1][vId] < distEstimate) {
469 | distEstimate = this.dist[0][vId] + this.dist[1][vId];
470 | currBest = vId;
471 | }
472 | });
473 |
474 | let last = currBest;
475 |
476 | console.log(distance, distEstimate, this.dist[0][last] + this.dist[1][last]);
477 |
478 | if (!this.parent[0][last] || !this.parent[1][last]) {
479 | console.log('No parent found WTF', last, this.parent[0][last], this.parent[1][last]);
480 | return { allEdges: this.allEdges, edges: this.edges };
481 | }
482 |
483 | while (last !== this.s) {
484 | this.addAllEdge(this.parent[0][last], last, 'edges');
485 | last = this.parent[0][last];
486 | }
487 |
488 | this.edges.reverse();
489 |
490 | last = currBest;
491 |
492 | while (last !== this.t) {
493 | this.addAllEdge(this.parent[1][last], last, 'edges');
494 | last = this.parent[1][last];
495 | }
496 |
497 | return { allEdges: [], edges: this.edges };
498 | }
499 | }
500 |
501 | export default ContractionHierarchy;
502 |
--------------------------------------------------------------------------------
/apps/preprocess/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "preprocess",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@protobufjs/aspromise": {
8 | "version": "1.1.2",
9 | "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
10 | "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
11 | },
12 | "@protobufjs/base64": {
13 | "version": "1.1.2",
14 | "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
15 | "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
16 | },
17 | "@protobufjs/codegen": {
18 | "version": "2.0.4",
19 | "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
20 | "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
21 | },
22 | "@protobufjs/eventemitter": {
23 | "version": "1.1.0",
24 | "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
25 | "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
26 | },
27 | "@protobufjs/fetch": {
28 | "version": "1.1.0",
29 | "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
30 | "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
31 | "requires": {
32 | "@protobufjs/aspromise": "^1.1.1",
33 | "@protobufjs/inquire": "^1.1.0"
34 | }
35 | },
36 | "@protobufjs/float": {
37 | "version": "1.0.2",
38 | "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
39 | "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
40 | },
41 | "@protobufjs/inquire": {
42 | "version": "1.1.0",
43 | "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
44 | "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
45 | },
46 | "@protobufjs/path": {
47 | "version": "1.1.2",
48 | "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
49 | "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
50 | },
51 | "@protobufjs/pool": {
52 | "version": "1.1.0",
53 | "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
54 | "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
55 | },
56 | "@protobufjs/utf8": {
57 | "version": "1.1.0",
58 | "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
59 | "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
60 | },
61 | "@types/long": {
62 | "version": "4.0.2",
63 | "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
64 | "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA=="
65 | },
66 | "@types/node": {
67 | "version": "10.17.60",
68 | "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
69 | "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
70 | },
71 | "async": {
72 | "version": "2.6.4",
73 | "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
74 | "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
75 | "requires": {
76 | "lodash": "^4.17.14"
77 | }
78 | },
79 | "call-bind-apply-helpers": {
80 | "version": "1.0.1",
81 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz",
82 | "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==",
83 | "requires": {
84 | "es-errors": "^1.3.0",
85 | "function-bind": "^1.1.2"
86 | }
87 | },
88 | "call-bound": {
89 | "version": "1.0.3",
90 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz",
91 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==",
92 | "requires": {
93 | "call-bind-apply-helpers": "^1.0.1",
94 | "get-intrinsic": "^1.2.6"
95 | }
96 | },
97 | "colors": {
98 | "version": "1.0.3",
99 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
100 | "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw=="
101 | },
102 | "corser": {
103 | "version": "2.0.1",
104 | "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz",
105 | "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ=="
106 | },
107 | "debug": {
108 | "version": "3.2.7",
109 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
110 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
111 | "requires": {
112 | "ms": "^2.1.1"
113 | }
114 | },
115 | "dunder-proto": {
116 | "version": "1.0.1",
117 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
118 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
119 | "requires": {
120 | "call-bind-apply-helpers": "^1.0.1",
121 | "es-errors": "^1.3.0",
122 | "gopd": "^1.2.0"
123 | }
124 | },
125 | "ecstatic": {
126 | "version": "3.3.2",
127 | "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-3.3.2.tgz",
128 | "integrity": "sha512-fLf9l1hnwrHI2xn9mEDT7KIi22UDqA2jaCwyCbSUJh9a1V+LEUSL/JO/6TIz/QyuBURWUHrFL5Kg2TtO1bkkog==",
129 | "requires": {
130 | "he": "^1.1.1",
131 | "mime": "^1.6.0",
132 | "minimist": "^1.1.0",
133 | "url-join": "^2.0.5"
134 | }
135 | },
136 | "es-define-property": {
137 | "version": "1.0.1",
138 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
139 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="
140 | },
141 | "es-errors": {
142 | "version": "1.3.0",
143 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
144 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="
145 | },
146 | "es-object-atoms": {
147 | "version": "1.1.1",
148 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
149 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
150 | "requires": {
151 | "es-errors": "^1.3.0"
152 | }
153 | },
154 | "eventemitter3": {
155 | "version": "4.0.7",
156 | "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
157 | "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
158 | },
159 | "follow-redirects": {
160 | "version": "1.15.9",
161 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
162 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ=="
163 | },
164 | "function-bind": {
165 | "version": "1.1.2",
166 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
167 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="
168 | },
169 | "get-intrinsic": {
170 | "version": "1.2.7",
171 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz",
172 | "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==",
173 | "requires": {
174 | "call-bind-apply-helpers": "^1.0.1",
175 | "es-define-property": "^1.0.1",
176 | "es-errors": "^1.3.0",
177 | "es-object-atoms": "^1.0.0",
178 | "function-bind": "^1.1.2",
179 | "get-proto": "^1.0.0",
180 | "gopd": "^1.2.0",
181 | "has-symbols": "^1.1.0",
182 | "hasown": "^2.0.2",
183 | "math-intrinsics": "^1.1.0"
184 | }
185 | },
186 | "get-proto": {
187 | "version": "1.0.1",
188 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
189 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
190 | "requires": {
191 | "dunder-proto": "^1.0.1",
192 | "es-object-atoms": "^1.0.0"
193 | }
194 | },
195 | "gopd": {
196 | "version": "1.2.0",
197 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
198 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="
199 | },
200 | "has-symbols": {
201 | "version": "1.1.0",
202 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
203 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="
204 | },
205 | "hasown": {
206 | "version": "2.0.2",
207 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
208 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
209 | "requires": {
210 | "function-bind": "^1.1.2"
211 | }
212 | },
213 | "he": {
214 | "version": "1.2.0",
215 | "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
216 | "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
217 | },
218 | "http-proxy": {
219 | "version": "1.18.1",
220 | "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
221 | "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
222 | "requires": {
223 | "eventemitter3": "^4.0.0",
224 | "follow-redirects": "^1.0.0",
225 | "requires-port": "^1.0.0"
226 | }
227 | },
228 | "http-server": {
229 | "version": "0.11.2",
230 | "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.11.2.tgz",
231 | "integrity": "sha512-Gp1ka7W4MLjFz8CLhFmUWa+uIf7cq93O4DZv8X0ZmNS1L4P2dbMkmlBeYhb0hGaI3M0Y1xM4waWgnIf/5Hp7dQ==",
232 | "requires": {
233 | "colors": "1.0.3",
234 | "corser": "~2.0.0",
235 | "ecstatic": "^3.0.0",
236 | "http-proxy": "^1.8.1",
237 | "opener": "~1.4.0",
238 | "optimist": "0.6.x",
239 | "portfinder": "^1.0.13",
240 | "union": "^0.5.0"
241 | }
242 | },
243 | "lodash": {
244 | "version": "4.17.21",
245 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
246 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
247 | },
248 | "long": {
249 | "version": "4.0.0",
250 | "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
251 | "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
252 | },
253 | "math-intrinsics": {
254 | "version": "1.1.0",
255 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
256 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="
257 | },
258 | "mime": {
259 | "version": "1.6.0",
260 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
261 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
262 | },
263 | "minimist": {
264 | "version": "1.2.8",
265 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
266 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="
267 | },
268 | "mkdirp": {
269 | "version": "0.5.6",
270 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
271 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
272 | "requires": {
273 | "minimist": "^1.2.6"
274 | }
275 | },
276 | "ms": {
277 | "version": "2.1.3",
278 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
279 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
280 | },
281 | "node-xml": {
282 | "version": "1.0.2",
283 | "resolved": "https://registry.npmjs.org/node-xml/-/node-xml-1.0.2.tgz",
284 | "integrity": "sha512-gVuB6EgkzcLmz0P4xxU+Zas2L3Hs0uLwWWub2XKWiIllvyr82qKLzLApr/H/a+I/QLUvVoa52NDN127c/yfyZw=="
285 | },
286 | "object-inspect": {
287 | "version": "1.13.3",
288 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz",
289 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA=="
290 | },
291 | "opener": {
292 | "version": "1.4.3",
293 | "resolved": "https://registry.npmjs.org/opener/-/opener-1.4.3.tgz",
294 | "integrity": "sha512-4Im9TrPJcjAYyGR5gBe3yZnBzw5n3Bfh1ceHHGNOpMurINKc6RdSIPXMyon4BZacJbJc36lLkhipioGbWh5pwg=="
295 | },
296 | "optimist": {
297 | "version": "0.6.1",
298 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
299 | "integrity": "sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==",
300 | "requires": {
301 | "minimist": "~0.0.1",
302 | "wordwrap": "~0.0.2"
303 | },
304 | "dependencies": {
305 | "minimist": {
306 | "version": "0.0.10",
307 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
308 | "integrity": "sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw=="
309 | }
310 | }
311 | },
312 | "osm-read": {
313 | "version": "0.7.0",
314 | "resolved": "https://registry.npmjs.org/osm-read/-/osm-read-0.7.0.tgz",
315 | "integrity": "sha512-/CzYsabjW27DtXoRKgAuJxS3y7DFrFDjM8WglEJpTR+z1vJUN2Ogya/SeqXQ3ZERijBQPlCrwPX7NnMamDSSxA==",
316 | "requires": {
317 | "http-server": "^0.11.1",
318 | "node-xml": "1.0.2",
319 | "protobufjs": "6.8.8",
320 | "setimmediate": "1.0.2",
321 | "zlibjs": "0.2.0"
322 | }
323 | },
324 | "portfinder": {
325 | "version": "1.0.32",
326 | "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz",
327 | "integrity": "sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==",
328 | "requires": {
329 | "async": "^2.6.4",
330 | "debug": "^3.2.7",
331 | "mkdirp": "^0.5.6"
332 | }
333 | },
334 | "protobufjs": {
335 | "version": "6.8.8",
336 | "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
337 | "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
338 | "requires": {
339 | "@protobufjs/aspromise": "^1.1.2",
340 | "@protobufjs/base64": "^1.1.2",
341 | "@protobufjs/codegen": "^2.0.4",
342 | "@protobufjs/eventemitter": "^1.1.0",
343 | "@protobufjs/fetch": "^1.1.0",
344 | "@protobufjs/float": "^1.0.2",
345 | "@protobufjs/inquire": "^1.1.0",
346 | "@protobufjs/path": "^1.1.2",
347 | "@protobufjs/pool": "^1.1.0",
348 | "@protobufjs/utf8": "^1.1.0",
349 | "@types/long": "^4.0.0",
350 | "@types/node": "^10.1.0",
351 | "long": "^4.0.0"
352 | }
353 | },
354 | "qs": {
355 | "version": "6.14.0",
356 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
357 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
358 | "requires": {
359 | "side-channel": "^1.1.0"
360 | }
361 | },
362 | "requires-port": {
363 | "version": "1.0.0",
364 | "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
365 | "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
366 | },
367 | "setimmediate": {
368 | "version": "1.0.2",
369 | "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.2.tgz",
370 | "integrity": "sha512-GREioum/m7Loh5Rbuza9pphdZ6V4eNmItSTv1CuV1V57BBe5WS+l/3MCN/sp9T/TRrKzm1mIyrxZRy+tbmfAuA==",
371 | "optional": true
372 | },
373 | "side-channel": {
374 | "version": "1.1.0",
375 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
376 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
377 | "requires": {
378 | "es-errors": "^1.3.0",
379 | "object-inspect": "^1.13.3",
380 | "side-channel-list": "^1.0.0",
381 | "side-channel-map": "^1.0.1",
382 | "side-channel-weakmap": "^1.0.2"
383 | }
384 | },
385 | "side-channel-list": {
386 | "version": "1.0.0",
387 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
388 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
389 | "requires": {
390 | "es-errors": "^1.3.0",
391 | "object-inspect": "^1.13.3"
392 | }
393 | },
394 | "side-channel-map": {
395 | "version": "1.0.1",
396 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
397 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
398 | "requires": {
399 | "call-bound": "^1.0.2",
400 | "es-errors": "^1.3.0",
401 | "get-intrinsic": "^1.2.5",
402 | "object-inspect": "^1.13.3"
403 | }
404 | },
405 | "side-channel-weakmap": {
406 | "version": "1.0.2",
407 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
408 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
409 | "requires": {
410 | "call-bound": "^1.0.2",
411 | "es-errors": "^1.3.0",
412 | "get-intrinsic": "^1.2.5",
413 | "object-inspect": "^1.13.3",
414 | "side-channel-map": "^1.0.1"
415 | }
416 | },
417 | "union": {
418 | "version": "0.5.0",
419 | "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz",
420 | "integrity": "sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==",
421 | "requires": {
422 | "qs": "^6.4.0"
423 | }
424 | },
425 | "url-join": {
426 | "version": "2.0.5",
427 | "resolved": "https://registry.npmjs.org/url-join/-/url-join-2.0.5.tgz",
428 | "integrity": "sha512-c2H1fIgpUdwFRIru9HFno5DT73Ok8hg5oOb5AT3ayIgvCRfxgs2jyt5Slw8kEB7j3QUr6yJmMPDT/odjk7jXow=="
429 | },
430 | "wordwrap": {
431 | "version": "0.0.3",
432 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
433 | "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw=="
434 | },
435 | "zlibjs": {
436 | "version": "0.2.0",
437 | "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.2.0.tgz",
438 | "integrity": "sha512-UA5w5YE92YlkBgr2qk4G3rNrOskQqHy0b/vhlU1U+MKeEHAASJ787MiXBUUsEuHO5t0pwKZ7VOAEJMJhLXcJsg==",
439 | "optional": true
440 | }
441 | }
442 | }
443 |
--------------------------------------------------------------------------------