diff --git a/modules/io/export.js b/modules/io/export.js index e7dfcc46..9920c4ad 100644 --- a/modules/io/export.js +++ b/modules/io/export.js @@ -465,9 +465,20 @@ function saveGeoJSON_Cells() { } function saveGeoJSON_Routes() { - const features = pack.routes.map(({id, points, cells, group}) => { - const coordinates = points || getRoutePoints(cells, group); - return {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, group}}; + 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]; + }); + + 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}; diff --git a/modules/routes-generator.js b/modules/routes-generator.js index 9e8c38af..2e87876f 100644 --- a/modules/routes-generator.js +++ b/modules/routes-generator.js @@ -124,35 +124,47 @@ window.Routes = (function () { function combineRoutes() { 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}); } - // merge routes so that the last cells of one route are the first cells of the next route - for (let i = 0; i < trails.length; i++) { - const {cells, feature, merged} = trails[i]; + for (const {feature, cells, merged} of mergeRoutes(trails)) { 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}); } - 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}); } 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) { const links = {}; diff --git a/modules/ui/layers.js b/modules/ui/layers.js index c0caf887..13672fa9 100644 --- a/modules/ui/layers.js +++ b/modules/ui/layers.js @@ -1645,10 +1645,17 @@ function drawRoutes() { const routePaths = {}; 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) { const {i, group} = route; lineGen.curve(ROUTE_CURVES[group] || ROUTE_CURVES.default); - const routePoints = getRoutePoints(route); + const routePoints = getRoutePoints(route, points); const path = round(lineGen(routePoints), 1); if (!routePaths[group]) routePaths[group] = []; @@ -1666,49 +1673,42 @@ function drawRoutes() { const ROUTES_SHARP_ANGLE = 135; const ROUTES_VERY_SHARP_ANGLE = 115; -function getRoutePoints({points, cells: cellIds, group}) { - if (points) return points; - const {cells, burgs} = pack; +function getRoutePoints(route, points) { + if (route.points) return route.points; + const routePoints = route.cells.map(cellId => points[cellId]); - const routePoints = cellIds.map(cellId => { - const burgId = cells.burg[cellId]; - if (burgId) { - const {x, y} = burgs[burgId]; - return [x, y]; - } + if (route.group !== "searoutes2") { + for (let i = 1; i < route.cells.length - 1; i++) { + const cellId = route.cells[i]; + if (pack.cells.burg[cellId]) continue; - return cells.p[cellId]; - }); + const [prevX, prevY] = routePoints[i - 1]; + const [currX, currY] = routePoints[i]; + const [nextX, nextY] = routePoints[i + 1]; - if (group !== "searoutes") { - for (let i = 1; i < cellIds.length - 1; i++) { - const cellId = cellIds[i]; - if (cells.burg[cellId]) continue; + const dAx = prevX - currX; + const dAy = prevY - currY; + const dBx = nextX - currX; + 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]; - const that = routePoints[i]; - const next = routePoints[i + 1]; + if (angle < ROUTES_SHARP_ANGLE) { + const middleX = (prevX + nextX) / 2; + const middleY = (prevY + nextY) / 2; + let newX, newY; - const dAx = prev[0] - that[0]; - const dAy = prev[1] - that[1]; - const dBx = next[0] - that[0]; - const dBy = next[1] - that[1]; - const angle = (Math.atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy) * 180) / Math.PI; - - 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; + if (angle < ROUTES_VERY_SHARP_ANGLE) { + newX = rn((currX + middleX * 2) / 3, 2); + newY = rn((currY + middleY * 2) / 3, 2); + } else { + newX = rn((currX + middleX) / 2, 2); + newY = rn((currY + middleY) / 2, 2); } - const newX = (that[0] + middleX) / 2; - const newY = (that[1] + middleY) / 2; - routePoints[i] = [newX, newY]; + if (findCell(newX, newY) === cellId) { + routePoints[i] = [newX, newY]; + points[cellId] = routePoints[i]; // change cell coordinate for all routes + } } } } diff --git a/modules/ui/routes-editor.js b/modules/ui/routes-editor.js index ffd51bfc..63b029ce 100644 --- a/modules/ui/routes-editor.js +++ b/modules/ui/routes-editor.js @@ -20,7 +20,7 @@ function editRoute(id) { updateRouteData(); - drawControlPoints(getRoutePoints(getRoute())); + drawControlPoints(getPoints()); drawCells(); $("#routeEditor").dialog({ @@ -49,6 +49,17 @@ function editRoute(id) { 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() { const route = getRoute();