diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index cfbe9c4..e237e9d 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -6,10 +6,6 @@
-
-
-
-
@@ -92,7 +88,7 @@
-
+
diff --git a/PublicProperty.js b/PublicProperty.js
index 5d5b69d..5d094a9 100644
--- a/PublicProperty.js
+++ b/PublicProperty.js
@@ -1,9 +1,11 @@
// no dependencies
export const __DEBUG__ = 1;
-export const __APP_VERSION__ = 'v0.2.0a';
+export const __APP_VERSION__ = 'v0.3.0a';
export const __APP_INTRO__ = `
Algorithm improvement.
-We are introducing a new pre-processing method called Obvious in this version.
+Our new routing solution is based on the Obvious A-Star algorithm now, with 2~10x faster speed in calculations.
+Support concatenating paths.
+You can add points sequentially on the map.
`;
\ No newline at end of file
diff --git a/api/click.js b/api/click.js
index 3d35706..812dd26 100644
--- a/api/click.js
+++ b/api/click.js
@@ -1,9 +1,9 @@
-import {dijkstra, haversine_distance, obvious_dijkstra} from "../tools/ShortestPath";
+import {dijkstra, haversine_distance, obvious_dijkstra, obvious_a_star} from "../tools/ShortestPath";
import {sill, sill_unwrap, noexcept} from "../tools/Debug";
import {get_row} from "../tools/Misc";
import benchmark from "../tools/PathBench";
-const __spa = obvious_dijkstra;
+const __spa = obvious_a_star;
function find_nearest_node_id(nodes, point) {
const [lat, lon] = point;
diff --git a/src/UMap.js b/src/UMap.js
index 976b544..4c94e8b 100644
--- a/src/UMap.js
+++ b/src/UMap.js
@@ -39,7 +39,7 @@ class Markers extends Component {
markers: [...prev.markers, [lat, lng]],
candMarkers: prev.candMarkers,
candEmpty: prev.candEmpty,
- polylines: [], // automatically clear polylines when marker changes
+ polylines: prev.polylines,
}));
this.getFocus();
}
@@ -89,12 +89,12 @@ class Markers extends Component {
}
}
- flushPolylines(pl) {
+ flushPolylines(pl, clear=true) {
this.setState((prev) => ({
markers: prev.markers,
candMarkers: prev.candMarkers,
candEmpty: prev.candEmpty,
- polylines: pl,
+ polylines: clear ? pl : [...pl, ...prev.polylines], // mind the ordering
}));
// TODO
}
@@ -107,7 +107,7 @@ function MapClickHandler({mks,focusUpdater,locator,locker}) {
const {lat,lng}=e.latlng;
console.info(`Clicking on ${lat} ${lng}`);
mks.current.addMarker(lat, lng);
- post('POST', 'click', mks.current.state.markers).then((response) => {
+ post('POST', 'click', mks.current.state.markers.slice(-2)).then((response) => {
// TODO: real functionality
const pl = JSON.parse(response.multipolyline);
// DEBUG
@@ -115,7 +115,7 @@ function MapClickHandler({mks,focusUpdater,locator,locker}) {
// mks.current.addCandMarker(lat,lon);
// });
sill(`pl = ${JSON.stringify(pl)}`);
- if (pl.length > 1) mks.current.flushPolylines(pl);
+ if (pl.length > 1) mks.current.flushPolylines(pl, false);
focusUpdater([lat,lng]);
locator([lat,lng]);
locker(true);
diff --git a/tools/PathBench.js b/tools/PathBench.js
index cc2e2b2..16f6181 100644
--- a/tools/PathBench.js
+++ b/tools/PathBench.js
@@ -5,15 +5,25 @@ import {get_row} from "./Misc";
export default function benchmark(nodes, clean_nodes, ways, location, ch_dict, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id) {
sill(`==========PathBench==========`);
let start_time, end_time;
- let res;
+ let res, _;
//benchmark Obvious-Dijkstra
start_time = performance.now();
res = SP.obvious_dijkstra(clean_nodes, ways, location, ch_dict, count, aff, actual_start_node_id, actual_end_node_id);
end_time = performance.now();
sill(`Obvious-Dijkstra run-time: ${end_time - start_time} ms`);
+ // benchmark Obvious-A-Star
+ start_time = performance.now();
+ res = SP.obvious_a_star(clean_nodes, ways, location, ch_dict, count, aff, actual_start_node_id, actual_end_node_id);
+ end_time = performance.now();
+ sill(`Obvious-A-Star run-time: ${end_time - start_time} ms`);
+ // benchmark Obvious-Adaptive-A-Star
+ start_time = performance.now();
+ _ = SP.obvious_a_star(clean_nodes, ways, location, ch_dict, count, aff, actual_start_node_id, actual_end_node_id, true);
+ end_time = performance.now();
+ sill(`Obvious-Adaptive-A-Star run-time: ${end_time - start_time} ms`);
// benchmark Dijkstra
start_time = performance.now();
- const _ = SP.dijkstra(nodes, ways, location, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id);
+ res = SP.dijkstra(nodes, ways, location, ch_dict_bench, count, aff, actual_start_node_id, actual_end_node_id);
end_time = performance.now();
sill(`Dijkstra run-time: ${end_time - start_time} ms`);
sill(`==============================`);
diff --git a/tools/ShortestPath.js b/tools/ShortestPath.js
index 94078a1..b28dc39 100644
--- a/tools/ShortestPath.js
+++ b/tools/ShortestPath.js
@@ -64,7 +64,7 @@ function __dijkstra(nodes, ways, loc, ch, count, aff, u, p) {
while (fa[curr]) {
curr = fa[curr].toString();
if (vis.has(curr)) {
- sill(`Cycle at ${curr}`);
+ // sill(`Cycle at ${curr}`);
break;
}
vis.add(curr);
@@ -125,7 +125,98 @@ function __obvious_dijkstra(nodes, ways, loc, ch, count, aff, u, p) {
const prev = curr;
curr = fa[curr];
if (vis.has(curr)) {
- sill(`Cycle at ${curr}`);
+ // sill(`Cycle at ${curr}`);
+ // sill(res);
+ break;
+ }
+ vis.set(curr,true);
+
+ // res.push(curr);
+ const way_id = find_common(aff[prev], aff[curr]);
+ const way = ways[way_id];
+ if(!way) {
+ continue;
+ }
+ const p_oc = loc[prev][way_id];
+ const c_oc = loc[curr][way_id];
+ if (p_oc < c_oc) {
+ for (let _p = p_oc + 1; _p <= c_oc; ++_p) {
+ res.push(way[_p]);
+ }
+ } else {
+ for (let _p = p_oc - 1; _p >= c_oc; --_p) {
+ res.push(way[_p]);
+ }
+ }
+ }
+ // sill("finished Obvious Dijkstra.");
+ return res;
+}
+
+/**
+ * Use A-star algorithm with Obvious optimization to find the shortest path.
+ * @param nodes node index list
+ * @param ways node list for each way
+ * @param loc relative location in the way
+ * @param ch adjacent table
+ * @param count determine isolated nodes
+ * @param aff affiliation of isolated nodes
+ * @param u start node id
+ * @param p destination node id
+ * @param adaptive if the coefficient is hard-coded
+ */
+function __obvious_a_star(nodes, ways, loc, ch, count, aff, u, p, adaptive = false) {
+ // sill(`node count: ${Object.keys(nodes).length}`);
+ const default_coefficient = 1.1;
+ const linear = (y) => y;
+ const curve = (y) => 0.8 * y * y;
+ const inversed_sigmoid = (y) => Math.log(y/(1.0-y));
+ const heuristic = (current_distance, node_id) => {
+ const estimate = haversine_distance(nodes[node_id], nodes[p]);
+ const coef = (!adaptive) ? default_coefficient : (
+ default_coefficient * curve(current_distance / (current_distance + estimate))
+ );
+ return coef * estimate;
+ };
+ const dis = {};
+ const fa = {};
+ const vis = new Map();
+ const pq = new MinPriorityQueue();
+ dis[u] = 0;
+ pq.push([0, 0, u]);
+ while (!pq.isEmpty()) {
+ const [_, d, v] = pq.pop();
+ if (vis.has(v) || !ch[v]) {
+ // sill(!ch[v]);
+ continue;
+ }
+ if (v === p) {
+ // sill('break');
+ break;
+ }
+ // sill('loop');
+ vis.set(v,true);
+ const t = ch[v].length;
+ for (let j = 0; j < t; ++j) {
+ const [c, w] = ch[v][j];
+ if (!nodes[c]) continue;
+ // sill(w);
+ if ((!dis[c] || d + w < dis[c])) {
+ dis[c] = d + w;
+ pq.push([dis[c] + heuristic(dis[c], c), dis[c], c]);
+ fa[c] = v;
+ }
+ }
+ }
+ // sill_unwrap(fa);
+ let curr = p;
+ const res = [p];
+ vis.clear();
+ while (fa[curr]) {
+ const prev = curr;
+ curr = fa[curr];
+ if (vis.has(curr)) {
+ // sill(`Cycle at ${curr}`);
// sill(res);
break;
}
@@ -155,4 +246,5 @@ function __obvious_dijkstra(nodes, ways, loc, ch, count, aff, u, p) {
export const haversine_distance = noexcept(__haversine_distance);
export const dijkstra = noexcept(__dijkstra);
-export const obvious_dijkstra = noexcept(__obvious_dijkstra);
\ No newline at end of file
+export const obvious_dijkstra = noexcept(__obvious_dijkstra);
+export const obvious_a_star = noexcept(__obvious_a_star);
\ No newline at end of file