Public
Edited
Jul 19, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
[
_EMCdata.RouteData.SydneySuburbanTestData.features[1],
_EMCdata.RouteData.SydneySuburban.features[1]
]
Insert cell
Insert cell
Insert cell
Input_SydneyTransitLinesGeoJSON.features
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Buffered = {
const outlined_features = [];
for (const feature of Input_SydneyTransitLinesGeoJSON.features) {
const route_geometry = JSTS_IO_HANDLERS.gjreader.read(feature.geometry);
const buffered_gemetry = route_geometry.buffer(
Args2_BufferParams.buffer_size
);
const new_feature = _Utils.Generate.GeoJSON(
"Feature",
JSTS_IO_HANDLERS.gjwriter.write(buffered_gemetry),
feature.properties
);
outlined_features.push(new_feature);
}
return _Utils.Generate.GeoJSON("FeatureCollection", outlined_features, {});
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_PreUnionsied_Geometries = SydneyTrainRoutes_Buffered.features.map(
(feature) => JSTS_IO_HANDLERS.gjreader.read(feature.geometry)
)
Insert cell
Insert cell
SydneyTrainRoutes_Unionised_Geometry = {
let ConstantGeometry = SydneyTrainRoutes_PreUnionsied_Geometries[0];
for (const CurrentGeometry of SydneyTrainRoutes_PreUnionsied_Geometries) {
ConstantGeometry = ConstantGeometry.union(CurrentGeometry);
}
//if (ConstantGeometry._geometries !== 1) throw new Error("GEOMETRIES DON'T INTERSECT");
return ConstantGeometry;
}
Insert cell
Insert cell
SydneyTrainRoutes_Unionised = {
const union_geometry = JSTS_IO_HANDLERS.gjwriter.write(
SydneyTrainRoutes_Unionised_Geometry
);
const union_feature = _Utils.Generate.GeoJSON("Feature", union_geometry, {});
return union_feature;
}
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Unionised
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Unionised_Densified = {
const Unionised_Points = //SydneyTrainRoutes_Unionised.geometry.coordinates
.map((coords) => {
const ls = _Imports.Geospatial.turf.lineString(coords);
const chunked = _Imports.Geospatial.turf.lineChunk(
ls,
Args41_DensificationParams.densify_to,
{
units: "kilometers"
}
);
return _Imports.Geospatial.turf.cleanCoords(
_Imports.Geospatial.turf.lineString(
chunked.features.map((f) => f.geometry.coordinates).flat()
)
);
})
.map((f) => f.geometry.coordinates);
return _Utils.Generate.GeoJSON(
"Feature",
{ type: "Polygon", coordinates: Unionised_Points },
{}
);
}
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Points = {
return _Imports.Geospatial.turf.explode(
SydneyTrainRoutes_Unionised_Densified
);
}
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Voronoi = {
return _Imports.Geospatial.geoDelaunay
.geoVoronoi(SydneyTrainRoutes_Centerline_A1_Points)
}
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Polygons = {
return SydneyTrainRoutes_Centerline_A1_Voronoi.triangles();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Polygons_Points = {
return _Imports.Geospatial.turf.pointsWithinPolygon(
_Imports.Geospatial.turf.multiPoint(
SydneyTrainRoutes_Centerline_A1_Polygons.features.map(
(f) => _Imports.Geospatial.turf.centerOfMass(f).geometry.coordinates
)
),
SydneyTrainRoutes_Unionised
).features[0];
}
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Polygons_Centers = {
return _Imports.Geospatial.turf.pointsWithinPolygon(
_Imports.Geospatial.turf.multiPoint(
SydneyTrainRoutes_Centerline_A1_Polygons.features.map(
(f) => f.properties.circumcenter
)
),
SydneyTrainRoutes_Unionised
).features[0];
}
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Polygons_Centeroid = {
return _Imports.Geospatial.turf.pointsWithinPolygon(
_Imports.Geospatial.turf.multiPoint(
SydneyTrainRoutes_Centerline_A1_Polygons.features.map(
(f) => _Imports.Geospatial.turf.centroid(f).geometry.coordinates
)
),
SydneyTrainRoutes_Unionised
).features[0];
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_PolygonCenters_Clipped = {
const Perf = new PerfMeasurement("Process_GeomClip");
console.group("Clipping Geometries");
console.log(
"Preparing to clip",
SydneyTrainRoutes_Centerline_A1_Polygons.features.length,
"geometries"
);
const Clipped = [];
for (const tri of SydneyTrainRoutes_Centerline_A1_Polygons.features) {
Perf.Start();
let mutableTri = tri;
if (
!_Imports.Geospatial.turf.booleanWithin(
_Imports.Geospatial.turf.centroid(tri),
SydneyTrainRoutes_Unionised
)
) {
Perf.End();
continue;
}

const PointsInTri = _Imports.Geospatial.turf
.cleanCoords(
_Imports.Geospatial.turf.multiPoint(tri.geometry.coordinates)
)
.geometry.coordinates[0].map((c) => {
return _Imports.Geospatial.turf.circle(
_Imports.Geospatial.turf.point(c),
Args44_ClipperParams.buffer_by,
{ units: "kilometers", steps: Args44_ClipperParams.poly_steps }
);
});

for (const ptc of PointsInTri.slice(0, 3)) {
const NmutableTri = _Imports.Geospatial.turf.difference(mutableTri, ptc);
if (NmutableTri !== null) mutableTri = NmutableTri;
}

Clipped.push(mutableTri);
console.log("Clipping Geometries...");
Perf.End();
}
console.groupEnd("Clipping Geometries");
return _Utils.Generate.GeoJSON(
"FeatureCollection",
Clipped.filter((c) => c !== null).flat(),
{
timings: Perf.GetTimings()
}
);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_Polygons_CenterPoints = {
const Perf = new PerfMeasurement("Process_PolyIntersecting");
const UnionLines = SydneyTrainRoutes_Unionised.geometry.coordinates.map(
(cs) => _Imports.Geospatial.turf.lineString(cs)
);

console.group("Processing polygon centers (iter 1)...");
console.log(
"Preparing to process",
SydneyTrainRoutes_Centerline_A1_Polygons_Points.geometry.coordinates.length,
"geometries"
);
const BufferedPoints =
SydneyTrainRoutes_Centerline_A1_Polygons_Points.geometry.coordinates.map(
(c) => {
Perf.Start();
let intersecting = false;
const point = _Imports.Geospatial.turf.buffer(
_Imports.Geospatial.turf.point(c),
0.001,
{ units: "kilometers" }
);
for (const line of UnionLines) {
if (intersecting) break;
intersecting = _Imports.Geospatial.turf.booleanIntersects(
line,
point
);
}
console.log("Processing Polygons...");
Perf.End();
if (intersecting) return null;
else return c;
}
);
const RemainingPoints = BufferedPoints.filter((c) => c !== null);
const discards =
SydneyTrainRoutes_Centerline_A1_Polygons_Points.geometry.coordinates
.length - RemainingPoints.length;
console.groupEnd("Processing polygon centers (iter 1)...");
return _Imports.Geospatial.turf.multiPoint(RemainingPoints, {
discarded: discards,
timings: Perf.GetTimings()
});
}
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_PolygonCenters = {
const Perf = new PerfMeasurement("Process_PolyCenterMass");
const tris = [];
console.group("Processing polygon centers (iter 2)...");
console.log(
"Preparing to process",
SydneyTrainRoutes_Centerline_A1_Polygons_CenterPoints.geometry.coordinates
.length,
"centers"
);
for (const coord of SydneyTrainRoutes_Centerline_A1_Polygons_CenterPoints
.geometry.coordinates) {
Perf.Start();
var pt = _Imports.Geospatial.turf.point(coord);
for (const poly of SydneyTrainRoutes_Centerline_A1_PolygonCenters_Clipped.features) {
if (_Imports.Geospatial.turf.booleanPointInPolygon(pt, poly) == true) {
tris.push(
_Imports.Geospatial.turf.feature(poly.geometry, {
center:
_Imports.Geospatial.turf.centerOfMass(poly).geometry.coordinates
})
);
}
}
console.log("Processing Centers...");
Perf.End();
}
console.groupEnd("Processing polygon centers (iter 2)...");
return {
res: tris.filter((c) => c !== null),
properties: { timings: Perf.GetTimings() }
};
}
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_A1_PolygonConnections = {
const Perf = new PerfMeasurement("Process_PolyIntersections");
const Intersecting = [];
console.group("Testing intersections...");
console.log(
"Preparing to process",
SydneyTrainRoutes_Centerline_A1_PolygonCenters.res.length,
"polygons"
);
for (const tri of SydneyTrainRoutes_Centerline_A1_PolygonCenters.res) {
Perf.Start();
for (const test of SydneyTrainRoutes_Centerline_A1_PolygonCenters.res) {
if (tri == test) continue;
const Lstr = _Imports.Geospatial.turf.lineString([
//_Imports.Geospatial.turf.centroid(tri).geometry.coordinates,
//_Imports.Geospatial.turf.centroid(test).geometry.coordinates,
tri.properties.center,
test.properties.center
]);
if (_Imports.Geospatial.turf.length(Lstr) > 0.5) continue;
if (
//!_Imports.Geospatial.turf.intersect(tri, test) &&
_Imports.Geospatial.turf.booleanOverlap(tri, test) ||
_Imports.Geospatial.turf.booleanIntersects(tri, test)
) {
//the two triangles connect!
Intersecting.push(Lstr);
}
}
console.log("Testing Intersections...");
Perf.End();
}
console.groupEnd("Testing intersections...");
return {
res: Intersecting,
properties: { timings: Perf.GetTimings() }
};
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_Centerline_Centerlines
Insert cell
Insert cell
SydneyTrainRoutes_ClSimplification_GapReducer = {
const Perf = new PerfMeasurement("Compute_PointCorrections");
console.group("Jumping gaps...");
console.log(
"Preparing to verify and correct",
SydneyTrainRoutes_Centerline_A1_PolygonCenters.res.length,
"lines"
);
const ReturnGeometry = [];

const PointsInitial = new Map();
const PointsFloaters = new Set();

const MaxDistance = 0.002;

const PointsExploded = _Imports.Geospatial.turf.explode(
SydneyTrainRoutes_Centerline_Centerlines
);

for (const point of PointsExploded.features) {
PointsInitial.set(point.geometry.coordinates.join(","), {
coords: point.geometry.coordinates,
links: new Set()
});
}

for (const link of SydneyTrainRoutes_Centerline_Centerlines.features) {
PointsInitial.get(link.geometry.coordinates[0].join(",")).links.add({
geom: link.geometry.coordinates[1],
infer: false
});
PointsInitial.get(link.geometry.coordinates[1].join(",")).links.add({
geom: link.geometry.coordinates[0],
infer: false
});
}

for (const [coord, point] of PointsInitial) {
if (point.links.size < 4) {
PointsFloaters.add(
_Imports.Geospatial.turf.point(point.coords, { coordID: coord })
);
}
}

for (const FloatingInitial of PointsFloaters.keys()) {
Perf.Start();
const PointsFiltered = _Imports.Geospatial.turf.featureCollection(
[...PointsFloaters].filter(
(pf) => pf.geometry.coordinates !== FloatingInitial.geometry.coordinates
)
);
const NearestPoint = _Imports.Geospatial.turf.nearestPoint(
FloatingInitial,
PointsFiltered
);
if (NearestPoint.properties.distanceToPoint > MaxDistance) continue;
console.log(
"Will fix broken node",
FloatingInitial.geometry.coordinates.join(","),
"with point",
NearestPoint.properties.distanceToPoint * 1000,
"m away"
);
PointsInitial.get(FloatingInitial.properties.coordID).links.add({
geom: NearestPoint.geometry.coordinates,
infer: true
});
PointsInitial.get(NearestPoint.properties.coordID).links.add({
geom: FloatingInitial.geometry.coordinates,
infer: true
});
Perf.End();
}

for (const [coord, point] of PointsInitial) {
for (const linkpt of point.links) {
ReturnGeometry.push(
_Imports.Geospatial.turf.lineString(
[point.coords, linkpt.geom],
linkpt.infer ? { stroke: "#ff0000" } : {}
)
);
}
}

console.groupEnd("Jumping gaps...");
return _Utils.Generate.GeoJSON("FeatureCollection", ReturnGeometry);
}
Insert cell
Insert cell
SydneyTrainRoutes_ClSimplification_UnifiedRoutes = {
console.groupCollapsed("Unifying routes...");
const Graph = _Imports.Graph.ngraph.graph();

const PointsExploded = _Imports.Geospatial.turf.explode(
SydneyTrainRoutes_ClSimplification_GapReducer
);

for (const point of PointsExploded.features) {
Graph.addNode(String(point.geometry.coordinates.join(",")), point);
}

for (const link of SydneyTrainRoutes_ClSimplification_GapReducer.features) {
Graph.addLink(
String(link.geometry.coordinates[0].join(",")),
String(link.geometry.coordinates[1].join(",")),
{
coordinates: [
link.geometry.coordinates[0],
link.geometry.coordinates[1]
]
}
);
Graph.addLink(
String(link.geometry.coordinates[1].join(",")),
String(link.geometry.coordinates[0].join(",")),
{
coordinates: [
link.geometry.coordinates[1],
link.geometry.coordinates[0]
]
}
);
}

console.log("Preparing to combine", PointsExploded.feature, "nodes");

const CoordCompare = (c1, c2) => {
return c1[0] == c2[0] && c1[1] == c2[1];
};

const weirdnodes = [];
const weirdlinks = [];

const junctions = [];

console.groupCollapsed("Weird Nodes 1");
Graph.forEachNode((node) => {
if (node.links.size < 4) {
console.log("weird node found - probably a terminus", node);
weirdnodes.push({
...node.data,
properties: {
links: node.links.size,
"marker-color": "#ffff00"
}
});
} else if (node.links.size > 4) {
console.log("weird node found - probably a junction", node);
weirdnodes.push({
...node.data,
properties: { links: node.links.size, "marker-color": "#00ffff" }
});
junctions.push(node.id);
}
//ClipReportTimes.push(performance.now() - PrevClipTime);
//PrevClipTime = performance.now();
});
console.groupEnd("Weird Nodes 1");

const MergeLinks = (iter) => {
const PerfMerge = new PerfMeasurement(`Combine_LinePairs_${iter}`);
console.log("Merging - Iteration", iter);
const MergeResults = {
skipped: 0,
success: 0,
failed: 0
};
const IterDbg = [0, PointsExploded.length * 2, 0];
Graph.forEachNode((node) => {
PerfMerge.Start();
let kickout = false;
if (node.links.size !== 4 || IterDbg[2] >= IterDbg[1]) {
MergeResults.skipped++;
kickout = true;
PerfMerge.End();
return;
}

//console.groupCollapsed("🔃 Identified node to merge -", node.id);
//console.log(node.id, node.data, node.links);
const CurLinks = [];
const Partners = new Set();
for (const link of node.links) {
if (link.toId == node.id) {
//console.log("Inbound Link from", link.fromId, link.data);
//CurLinks.push(link);
Partners.add(link.fromId);
} else if (link.fromId == node.id) {
//console.log("Outbound Link to", link.toId, link.data);
//CurLinks.push(link);
Partners.add(link.toId);
}
}
//console.log("Replacing with link pairs");
for (const partner of Partners) {
//console.groupCollapsed("🔃 Merge -", partner);
//console.log("Link partner", partner);
const NewCoordData = [];
let OtherNodeId = "";
for (const link of node.links) {
if (kickout == true) break;
if (link.fromId == partner && link.toId == node.id) {
//console.log("Potential merge A", link);
const LinkDataCoords = link.data.coordinates;

if (junctions.includes(partner)) {
console.log(
"⛔ Merge A: Junctions - Attempted to merge with a Junction",
LinkDataCoords
);
kickout = true;
}

if (
!CoordCompare(
LinkDataCoords[0],
Graph.getNode(partner).data.geometry.coordinates
)
) {
LinkDataCoords.reverse();
if (
!CoordCompare(
LinkDataCoords[0],
Graph.getNode(partner).data.geometry.coordinates
)
) {
console.log(
"⛔ Merge A: Coordinates - Still aren't ordered correctly.",
LinkDataCoords
);
kickout = true;
}
}

if (
!CoordCompare(
LinkDataCoords[LinkDataCoords.length - 1],
node.data.geometry.coordinates
)
) {
console.log(
"⛔ Merge A: Coordinates don't end at node to be removed",
LinkDataCoords,
node.data.geometry.coordinates,
LinkDataCoords[LinkDataCoords.length - 1]
);
kickout = true;
} else {
CurLinks.push(link);
}

NewCoordData[0] = LinkDataCoords;
}
if (link.fromId == node.id && link.toId !== partner) {
//console.log("Potential merge B", link);
OtherNodeId = link.toId;
const LinkDataCoords = link.data.coordinates;

if (junctions.includes(OtherNodeId)) {
console.log(
"⛔ Merge B: Junctions - Attempted to merge with a Junction",
LinkDataCoords
);
kickout = true;
}

if (
!CoordCompare(
LinkDataCoords[LinkDataCoords.length - 1],
Graph.getNode(OtherNodeId).data.geometry.coordinates
)
) {
LinkDataCoords.reverse();
if (
!CoordCompare(
LinkDataCoords[LinkDataCoords.length - 1],
Graph.getNode(OtherNodeId).data.geometry.coordinates
)
) {
console.log(
"⛔ Merge B: Coordinates still aren't ordered correctly",
LinkDataCoords,
node.data.geometry.coordinates
);
kickout = true;
}
}

if (
!CoordCompare(LinkDataCoords[0], node.data.geometry.coordinates)
) {
console.log(
"⛔ Merge B: Coordinates don't start at node to be removed",
LinkDataCoords,
node.data.geometry.coordinates,
LinkDataCoords[LinkDataCoords.length - 1]
);
kickout = true;
} else {
CurLinks.push(link);
}

NewCoordData[1] = LinkDataCoords;
}
}
if (kickout == false) {
Graph.addLink(String(partner), String(OtherNodeId), {
coordinates: NewCoordData.flat()
});
/*console.log(
"🟢 Link merged:",
NewCoordData.flat(),
"\n➡️ From:",
partner,
"\n⬅️ To:",
OtherNodeId
);
} else {*/
}
//console.groupEnd("🔃 Merge -", partner);
}

if (kickout == false) {
//console.log("Successfully merged!");
CurLinks.map((l) => Graph.removeLink(l));
Graph.removeNode(node.id);
MergeResults.success++;
} else {
//console.log("⛔ Kickout engaged. Node error, see logs.");
MergeResults.failed++;
}

IterDbg[2]++;
//console.groupEnd("🔃 Identified node to merge -", node.id);
PerfMerge.End();
});
//console.groupEnd("Merging - Iteration", iter);
return MergeResults;
};

const Results = {
skipped: [0],
success: [0],
failed: [0],
iters: 0,
updates: true
};

while (Results.updates == true) {
const newResults = MergeLinks(Results.iters);
if (
newResults.skipped !== Results.skipped[Results.skipped.length - 1] ||
newResults.success !== Results.success[Results.success.length - 1] ||
newResults.failed !== Results.failed[Results.failed.length - 1]
) {
Results.updates = true;
Results.skipped.push(newResults.skipped);
Results.success.push(newResults.success);
Results.failed.push(newResults.failed);
} else {
Results.updates = false;
}
Results.iters++;
}
/*console.table({
Skipped: Results.skipped,
Successful: Results.success,
Failed: Results.failed,
Iterations: Results.iters
});*/
console.groupCollapsed("Weird Nodes");
Graph.forEachNode((node) => {
if (node.links.size < 4) {
console.log("weird node found - probably a terminus", node);
weirdnodes.push({
...node.data,
properties: {
links: node.links.size,
"marker-color": "#ff0000"
}
});
} else if (node.links.size > 4) {
console.log("weird node found - probably a junction", node);
weirdnodes.push({
...node.data,
properties: { links: node.links.size, "marker-color": "#0000ff" }
});
}
});
console.groupEnd("Weird Nodes");

console.groupCollapsed("Weird Links");
Graph.forEachLink(function (link) {
if (link.data.coordinates.length < 4) {
console.log("weird link found - probably a single link", link);
weirdlinks.push(
_Imports.Geospatial.turf.lineString(link.data.coordinates, {
stroke: "#ff0000"
})
);
}
});
console.groupEnd("Weird Links");

/*return _Utils.Generate.GeoJSON("FeatureCollection", [
...SydneyTrainRoutes_ClSimplification_GapReducer.features,
...weirdnodes
]);*/

const ReturnGeometry = [];
Graph.forEachLink(function (link) {
ReturnGeometry.push(
_Imports.Geospatial.turf.lineString(link.data.coordinates)
);
});
console.groupEnd("Unifying routes...");
return _Utils.Generate.GeoJSON("FeatureCollection", [
...ReturnGeometry
//...weirdnodes,
//...weirdlinks
]);
}
Insert cell
SydneyTrainRoutes_ClSimplification_URWithoutSpurs = {
const UnifiedRoutes = _Imports.Geospatial.turf.clone(
SydneyTrainRoutes_ClSimplification_UnifiedRoutes
);
const ReturnGeometry = [];

for (const line of UnifiedRoutes.features) {
const length = _Imports.Geospatial.turf.length(line);
if (length < 0.05 && line.geometry.coordinates.length == 2) {
console.log(length);
/*const idx = UnifiedRoutes.features.indexOf(line);
if (idx > -1) {
UnifiedRoutes.features.splice(idx, 1);
console.log("Removed", line);
}*/
}
ReturnGeometry.push(line);
}

return _Utils.Generate.GeoJSON("FeatureCollection", ReturnGeometry);
}
Insert cell
SydneyTrainRoutes_ClSimplification_URDeduped = {
console.groupCollapsed("Removing nodes...");
const UnifiedRoutes = _Imports.Geospatial.turf.clone(
SydneyTrainRoutes_ClSimplification_URWithoutSpurs
);
const ReturnGeometry = [];

for (const line of UnifiedRoutes.features) {
for (const testLine of UnifiedRoutes.features) {
const test = _Imports.Geospatial.turf.lineOverlap(line, testLine);
if (test.features.length !== 0) {
if (test.features[0].geometry.coordinates.length >= 2) {
const idx = UnifiedRoutes.features.indexOf(testLine);
if (idx > -1) {
UnifiedRoutes.features.splice(idx, 1);
console.log("Removed node"); //, test);
}
}
}
}
ReturnGeometry.push(line);
}

console.groupEnd("Removing nodes...");
return _Utils.Generate.GeoJSON("FeatureCollection", ReturnGeometry);
}
Insert cell
viewof MapView_SydneyTrainRoutes_ClSimplification_UnifiedRoutes = {
if (!nbPerfOptions.showMaps) {
yield htl.html`<code>Maps are disabled</code>`;
return;
}
await visibility();
const map = new _MapLibre({ height: 500, zoom: 10 });
yield map.attach;

let hoveredStateId = null;

map.AddDataSource(
"simplified_outline",
SydneyTrainRoutes_ClSimplification_URDeduped
);
map.AddMapLine("simplified_outline", "simplified_outline", "#000", 5, 1);

invalidation.then(map.kill);
}
Insert cell
toSmooth = import("to-smooth")
Insert cell
SydneyTrainRoutes_ClSimplification_URSmoothed = {
const ReturnGeometry = [];
const ReturnOtherGeometry = [];
const FloatingLines = [];

const CoordCompare = (c1, c2) => {
return c1[0] == c2[0] && c1[1] == c2[1];
};

const RejectedLinesThreshold = 8;
const RejectedLinesLength = 0.1;
const CollusionProximity = 0.075;

for (const line of SydneyTrainRoutes_ClSimplification_URDeduped.features) {
if (
line.geometry.coordinates.length <= RejectedLinesThreshold &&
_Imports.Geospatial.turf.length(line) <= RejectedLinesLength
) {
FloatingLines.push(line);
}
}
for (const line of SydneyTrainRoutes_ClSimplification_URDeduped.features) {
if (
line.geometry.coordinates.length <= RejectedLinesThreshold &&
_Imports.Geospatial.turf.length(line) <= RejectedLinesLength
) {
ReturnOtherGeometry.push({
...line,
properties: { stroke: "#ff0000", "stroke-width": "5" }
});
} else {
const coordsArr =
_Imports.Geospatial.turf.cleanCoords(line).geometry.coordinates;
const Endpoints = [coordsArr[0], coordsArr[coordsArr.length - 1]];
const lnReduce = _Imports.Geospatial.turf.segmentReduce(
_Imports.Geospatial.turf.lineString(
toSmooth.default(coordsArr, { iteration: 3, factor: 0.75 })
),
(previousSegment, currentSegment) => {
const PrvSeg = previousSegment;
const CurSegCenter = _Imports.Geospatial.turf.center(currentSegment);
PrvSeg.geometry.coordinates.push(CurSegCenter.geometry.coordinates);
return PrvSeg;
}
);
const DeTriangulatedGeometry = _Imports.Geospatial.turf.lineString([
Endpoints[0],
...lnReduce.geometry.coordinates,
Endpoints[1]
]);
const SimplifiedGeometry = _Imports.Geospatial.turf.simplify(
DeTriangulatedGeometry,
{
tolerance: 0.00025,
highQuality: true
}
);

const ExtraPointFinder = (pcrds) => {
return [
FloatingLines.reduce(
(prev, cur) => {
const curDistance = _Imports.Geospatial.turf.pointToLineDistance(
_Imports.Geospatial.turf.point(pcrds),
cur
);
if (prev.properties.distance >= curDistance)
return { ...cur, properties: { distance: curDistance } };
else return prev;
},
{
geometry: { coordinates: pcrds },
properties: { distance: 696969696 }
}
)
]
.map((pnt) => {
if (pnt.properties.distance > CollusionProximity) {
return null;
} else {
return _Imports.Geospatial.turf
.cleanCoords(pnt)
.geometry.coordinates //.reverse()
.filter((crd) => !CoordCompare(crd, pcrds))
.flat();
}
})
.flat();
};
const GeomCoords = [0, 0];
const BeforePoints = {
BeforeStartPoint: ExtraPointFinder(Endpoints[0]),
BeforeEndPoint: ExtraPointFinder(Endpoints[1])
};
const BeforeBefore = {
BeforeStartPoint: [],
BeforeEndPoint: []
};
if (!BeforePoints.BeforeStartPoint.includes(null)) {
/*console.log(
_Imports.Geospatial.turf.distance(
_Imports.Geospatial.turf.point(BeforePoints.BeforeStartPoint),
_Imports.Geospatial.turf.point(Endpoints[0])
)
);*/
GeomCoords[0] = BeforePoints.BeforeStartPoint;
const SearchPoint = ExtraPointFinder(BeforePoints.BeforeStartPoint);
if (!SearchPoint.includes(null)) {
//GeomCoords[0];
}
}
if (!BeforePoints.BeforeEndPoint.includes(null)) {
/*console.log(
_Imports.Geospatial.turf.distance(
_Imports.Geospatial.turf.point(BeforePoints.BeforeEndPoint),
_Imports.Geospatial.turf.point(Endpoints[1])
)
);*/
GeomCoords[1] = BeforePoints.BeforeEndPoint;
}
//console.log(GeomCoords[0], GeomCoords[GeomCoords.length - 1]);

// fix my shitty programming

const GeomCoordsFinal = [];
const GeomCoordsInter = [
GeomCoords[0],
...toSmooth.default(SimplifiedGeometry.geometry.coordinates, {
iteration: 10,
factor: 0.75
}),
GeomCoords[1]
].filter((crd) => !(crd == null));

const CoordLengthsDebug = [];

GeomCoordsInter.map((c) => {
if (!CoordLengthsDebug.includes(c.length))
CoordLengthsDebug.push(c.length);

if (c.length == 4) {
GeomCoordsFinal.push([c[0], c[1]]);
GeomCoordsFinal.push([c[2], c[3]]);
} else if (c.length == 2) {
GeomCoordsFinal.push([c[0], c[1]]);
}
});

const ChaikenedGeometry = _Imports.Geospatial.turf.lineString(
GeomCoordsFinal,
{ /*stroke: "#ffffff",*/ "stroke-width": "2.5" }
);
ReturnGeometry.push(ChaikenedGeometry);
}
}

return _Utils.Generate.GeoJSON("FeatureCollection", [
_Imports.Geospatial.turf.combine(
_Imports.Geospatial.turf.featureCollection(ReturnGeometry)
).features[0]
//_Imports.Geospatial.turf.combine(
// _Imports.Geospatial.turf.featureCollection(ReturnOtherGeometry)
//).features[0]
]);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
SydneyTrainRoutes_ClBeautified_OriginRouteWays = SydneyTrainRoutes_ClSimplified
.features[0]
Insert cell
SydneyTrainRoutes_ClBeautified_OriginRouteJunctions = SydneyTrainRoutes_ClSimplified
.features[1]
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