├── .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 | 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 | 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 | 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 | 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 | ![Screenshot of the application](docs/screenshot.png?raw=true) 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 | 180 | setShowAlgoTrace(e.target.checked)}> 181 | Show visualization? 182 | 183 | setShowAnimation(e.target.checked)}> 184 | Animate? 185 | 186 | 187 | 188 | 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 | --------------------------------------------------------------------------------