Refactor route merging logic for improved performance

This commit is contained in:
Azgaar 2024-05-23 03:33:54 +02:00
parent 29e7ab0953
commit 4e79e020e8
4 changed files with 91 additions and 57 deletions

View file

@ -465,9 +465,20 @@ function saveGeoJSON_Cells() {
} }
function saveGeoJSON_Routes() { function saveGeoJSON_Routes() {
const features = pack.routes.map(({id, points, cells, group}) => { const {cells, burgs} = pack;
const coordinates = points || getRoutePoints(cells, group); let points = cells.p.map(([x, y], cellId) => {
return {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, group}}; const burgId = cells.burg[cellId];
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
return [x, y];
});
const features = pack.routes.map(route => {
const coordinates = route.points || getRoutePoints(route, points);
return {
type: "Feature",
geometry: {type: "LineString", coordinates},
properties: {id: route.id, group: route.group}
};
}); });
const json = {type: "FeatureCollection", features}; const json = {type: "FeatureCollection", features};

View file

@ -124,35 +124,47 @@ window.Routes = (function () {
function combineRoutes() { function combineRoutes() {
const routes = []; const routes = [];
for (const {feature, cells} of mainRoads) { for (const {feature, cells, merged} of mergeRoutes(mainRoads)) {
if (merged) continue;
routes.push({i: routes.length, group: "roads", feature, cells}); routes.push({i: routes.length, group: "roads", feature, cells});
} }
// merge routes so that the last cells of one route are the first cells of the next route for (const {feature, cells, merged} of mergeRoutes(trails)) {
for (let i = 0; i < trails.length; i++) {
const {cells, feature, merged} = trails[i];
if (merged) continue; if (merged) continue;
for (let j = i + 1; j < trails.length; j++) {
const nextTrail = trails[j];
if (nextTrail.merged) continue;
if (nextTrail.cells[0] === cells.at(-1)) {
cells.push(...nextTrail.cells.slice(1));
nextTrail.merged = true;
}
}
routes.push({i: routes.length, group: "trails", feature, cells}); routes.push({i: routes.length, group: "trails", feature, cells});
} }
for (const {feature, cells} of seaRoutes) { for (const {feature, cells, merged} of mergeRoutes(seaRoutes)) {
if (merged) continue;
routes.push({i: routes.length, group: "searoutes", feature, cells}); routes.push({i: routes.length, group: "searoutes", feature, cells});
} }
return routes; return routes;
} }
// merge routes so that the last cell of one route is the first cell of the next route
function mergeRoutes(routes) {
let routesMerged = 0;
for (let i = 0; i < routes.length; i++) {
const thisRoute = routes[i];
if (thisRoute.merged) continue;
for (let j = i + 1; j < routes.length; j++) {
const nextRoute = routes[j];
if (nextRoute.merged) continue;
if (nextRoute.cells.at(0) === thisRoute.cells.at(-1)) {
routesMerged++;
thisRoute.cells = thisRoute.cells.concat(nextRoute.cells.slice(1));
nextRoute.merged = true;
}
}
}
return routesMerged > 1 ? mergeRoutes(routes) : routes;
}
function buildLinks(routes) { function buildLinks(routes) {
const links = {}; const links = {};

View file

@ -1645,10 +1645,17 @@ function drawRoutes() {
const routePaths = {}; const routePaths = {};
const lineGen = d3.line(); const lineGen = d3.line();
const {cells, burgs} = pack;
let points = cells.p.map(([x, y], cellId) => {
const burgId = cells.burg[cellId];
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
return [x, y];
});
for (const route of pack.routes) { for (const route of pack.routes) {
const {i, group} = route; const {i, group} = route;
lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default); lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default);
const routePoints = getRoutePoints(route); const routePoints = getRoutePoints(route, points);
const path = round(lineGen(routePoints), 1); const path = round(lineGen(routePoints), 1);
if (!routePaths[group]) routePaths[group] = []; if (!routePaths[group]) routePaths[group] = [];
@ -1666,49 +1673,42 @@ function drawRoutes() {
const ROUTES_SHARP_ANGLE = 135; const ROUTES_SHARP_ANGLE = 135;
const ROUTES_VERY_SHARP_ANGLE = 115; const ROUTES_VERY_SHARP_ANGLE = 115;
function getRoutePoints({points, cells: cellIds, group}) { function getRoutePoints(route, points) {
if (points) return points; if (route.points) return route.points;
const {cells, burgs} = pack; const routePoints = route.cells.map(cellId => points[cellId]);
const routePoints = cellIds.map(cellId => { if (route.group !== "searoutes2") {
const burgId = cells.burg[cellId]; for (let i = 1; i < route.cells.length - 1; i++) {
if (burgId) { const cellId = route.cells[i];
const {x, y} = burgs[burgId]; if (pack.cells.burg[cellId]) continue;
return [x, y];
}
return cells.p[cellId]; const [prevX, prevY] = routePoints[i - 1];
}); const [currX, currY] = routePoints[i];
const [nextX, nextY] = routePoints[i + 1];
if (group !== "searoutes") { const dAx = prevX - currX;
for (let i = 1; i < cellIds.length - 1; i++) { const dAy = prevY - currY;
const cellId = cellIds[i]; const dBx = nextX - currX;
if (cells.burg[cellId]) continue; const dBy = nextY - currY;
const angle = Math.abs((Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI);
const prev = routePoints[i - 1]; if (angle < ROUTES_SHARP_ANGLE) {
const that = routePoints[i]; const middleX = (prevX + nextX) / 2;
const next = routePoints[i + 1]; const middleY = (prevY + nextY) / 2;
let newX, newY;
const dAx = prev[0] - that[0]; if (angle < ROUTES_VERY_SHARP_ANGLE) {
const dAy = prev[1] - that[1]; newX = rn((currX + middleX * 2) / 3, 2);
const dBx = next[0] - that[0]; newY = rn((currY + middleY * 2) / 3, 2);
const dBy = next[1] - that[1]; } else {
const angle = (Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI; newX = rn((currX + middleX) / 2, 2);
newY = rn((currY + middleY) / 2, 2);
if (Math.abs(angle) < ROUTES_SHARP_ANGLE) {
const middleX = (prev[0] + next[0]) / 2;
const middleY = (prev[1] + next[1]) / 2;
if (Math.abs(angle) < ROUTES_VERY_SHARP_ANGLE) {
const newX = (that[0] + middleX * 2) / 3;
const newY = (that[1] + middleY * 2) / 3;
routePoints[i] = [newX, newY];
continue;
} }
const newX = (that[0] + middleX) / 2; if (findCell(newX, newY) === cellId) {
const newY = (that[1] + middleY) / 2; routePoints[i] = [newX, newY];
routePoints[i] = [newX, newY]; points[cellId] = routePoints[i]; // change cell coordinate for all routes
}
} }
} }
} }

View file

@ -20,7 +20,7 @@ function editRoute(id) {
updateRouteData(); updateRouteData();
drawControlPoints(getRoutePoints(getRoute())); drawControlPoints(getPoints());
drawCells(); drawCells();
$("#routeEditor").dialog({ $("#routeEditor").dialog({
@ -49,6 +49,17 @@ function editRoute(id) {
return route; return route;
} }
function getPoints() {
const {cells, burgs} = pack;
let points = cells.p.map(([x, y], cellId) => {
const burgId = cells.burg[cellId];
if (burgId) return [burgs[burgId].x, burgs[burgId].y];
return [x, y];
});
return getRoutePoints(getRoute(), points);
}
function updateRouteData() { function updateRouteData() {
const route = getRoute(); const route = getRoute();