Published
Edited
Oct 22, 2020
1 fork
2 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
railLine
Insert cell
// Ignore first point because it's in here twice to close the polygon
outerRing = railLine.geometry.coordinates[0]
.slice(1)
.map(applyProjection);
Insert cell
Insert cell
Insert cell
Insert cell
voronoi = {
const [xMin, xMax] = d3.extent(pointsOnPolygon.map(d => d[0]));
const [yMin, yMax] = d3.extent(pointsOnPolygon.map(d => d[1]));

const leftTop = [xMin - 1, yMin - 1];
const rightBottom = [xMax + 1, yMax + 1];

return d3.voronoi().extent([leftTop, rightBottom])(pointsOnPolygon).edges;
}
Insert cell
filteredEdges = voronoi
.filter(edge => {
if (edge && edge.right) {
// array of booleans
const inside = edge.map(point =>
d3.polygonContains(pointsOnPolygon, point)
);

// necessary for corners
if (inside[0] === inside[1]) {
return inside[0]; // boolean
}

// again why?
if (inside[1]) {
edge.reverse();
}
return true;
}
return false;
})

Insert cell
// Clip voronoi edges to polygon
clippedEdges = filteredEdges.map(([start, end] = []) => {
const { intersection, distance } = originalFindClosestPolygonIntersection(
start,
end,
pointsOnPolygon
);

if (intersection) {
intersection.clipped = true;
}

// Each edge has a starting point, a clipped end point, and an original end point
const edge = [start, intersection || end];
edge.distance = intersection ? distance : distanceBetween(start, end);

return edge;
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nodes = {
const nodes = [];

// loop over clipped voronoi edges
clippedEdges.forEach(edge => {
console.log(edge[0])
// add Nodes of each Edge to Nodes array
edge.forEach((node, i) => {
// if no index or if not is not clipped
// so this is only for innerNodes and first node (0th)
if (!i || !node.clipped) {
// check if node is in nodes array, if so return element
// this is to set same id for wellconnected nodes
const nodeInNodes = nodes.find(d => d === node);
if (nodeInNodes) { // if node is in nodes array
return (node.id = nodeInNodes.id); // set node id equal to one already in nodes array
}
}
node.id = nodes.length.toString();
node.links = {};
nodes.push(node);
});
const startNode = edge[0];
const endNode = edge[1];
console.log(edge[0])
// set links to connected node
startNode.links[endNode.id] = edge.distance;
endNode.links[edge[0].id] = edge.distance;
});

return nodes;
}
Insert cell
graph = {
const graph = new Graph();
nodes.forEach(node => graph.addNode(node.id, node.links));
return graph;
}
Insert cell
innerNodes = nodes.filter(d => !d.clipped)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// prefer Nodes with more links over those with just one!
traversal = {
let totalBest;

for (let i = 0; i < perimeterNodes.length; i++) {
const start = perimeterNodes[i];
const longestShortestPath = perimeterNodes.slice(i + 1).reduce((nodeBest, node) => {
const path = graph.path(node.id, start.id, { cost: true });
if (path && (!nodeBest || path.cost > nodeBest.cost)) {
return path;
}
return nodeBest;
}, null);

if (longestShortestPath && longestShortestPath.path) {
longestShortestPath.path = longestShortestPath.path.map(id => nodes[+id]);

// Ignore fitnessFunction for now
if (!totalBest || longestShortestPath.cost > totalBest.cost) {
totalBest = longestShortestPath;
}
yield Promises.delay(+speed, {
bestPath: totalBest.path,
currentPath: longestShortestPath.path
});
}
}
if (totalBest) {
yield {
bestPath: totalBest.path
};
}
}
Insert cell
Insert cell
// ideally, wait until bestPath is finished walking the graph
centerlineTraversalGeo = traversal.bestPath.map(removeProjection)
Insert cell
Insert cell
centerlineTraversalGeoJSON = turf.lineString(centerlineTraversalGeo)
Insert cell
Insert cell
Insert cell
// innerNodesSvgLine = d3.pairs(innerNodes) // for svg line,
Insert cell
// geoPointsCenterline = innerNodes.map(removeProjection)
Insert cell
// centerlineGeoJSON = turf.lineString(geoPointsCenterline)
Insert cell
Insert cell
md`### Reading material

https://smathermather.com/2011/09/16/what-is-the-center-line-of-a-polygon-or-how-to-change-labeling-in-geoserver/

`
Insert cell
Insert cell
Insert cell
railNum
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
t = projection.translate();
Insert cell
s = projection.scale();
Insert cell
applyProjection = ([x, y]) => [s * x + t[0], s * y + t[1]]
Insert cell
removeProjection = ([x, y]) => [(x - t[0])/s, (y - t[1])/s]
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
turf = require("@turf/turf@5")
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more