Public
Edited
Mar 26, 2024
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
CompareCoords = {
return (c1, c2) => {
return c1[0] == c2[0] && c1[1] == c2[1];
};
}
Insert cell
ClipLine = {
return (line, distanceFromEdges) => {
if (_Imports.Geospatial.turf.length(line) <= distanceFromEdges * 3) {
return line;
} else {
const StartDistance = _Imports.Geospatial.turf.along(
line,
Math.min(
(_Imports.Geospatial.turf.length(line) / 10) * 4,
distanceFromEdges
)
);
const EndDistance = _Imports.Geospatial.turf.along(
line,
Math.max(
(_Imports.Geospatial.turf.length(line) / 10) * 6,
_Imports.Geospatial.turf.length(line) - distanceFromEdges
)
);

const SlicedLine = _Imports.Geospatial.turf.lineSlice(
StartDistance,
EndDistance,
line
);
try {
return _Imports.Geospatial.turf.cleanCoords(
SlicedLine.geometry.coordinates.length > 4 ? SlicedLine : line
);
} catch (e) {
return line;
}
}
};
}
Insert cell
LineSegments = {
const Segments = new Map();

// add arcs as nodes
for (let i = 0; i < LinesAsTopoJson.arcs.length; i++) {
Segments.set(`Segment:${i}`, {
geometry: LinesAsTopoJson.arcs[i],
top: LinesAsTopoJson.arcs[i][0],
bottom: LinesAsTopoJson.arcs[i][LinesAsTopoJson.arcs[i].length - 1],
colors: [
...new Set(
LinesAsTopoJson.objects.lines.geometries.map((line) => {
if (i < 0 ? line.arcs.includes(i * -1 - 1) : line.arcs.includes(i))
return line.properties.stroke || "#000000";
})
)
].filter((e) => e != null)
});
}

return Segments;
}
Insert cell
Insert cell
LineConnections = {
const Connections = [];

const ConnectionsDedupe = [];

for (const line of LinesAsTopoJson.objects.lines.geometries) {
line.arcs.reduce((acc, cur) => {
const fromId = acc < 0 ? acc * -1 - 1 : acc;
const fromPart = acc < 0 ? "top" : "bottom";
const toId = cur < 0 ? cur * -1 - 1 : cur;
const toPart = cur < 0 ? "bottom" : "top";

const DedupeString = [
line.properties.stroke,
fromId,
fromPart,
toId,
toPart
].join(":");

if (!ConnectionsDedupe.includes(DedupeString)) {
Connections.push({
from: [fromId, fromPart],
to: [toId, toPart],
color: line.properties.stroke
});
ConnectionsDedupe.push(DedupeString);
}

return cur;
});
}
return Connections;
}
Insert cell
RenderLines = {
return (spi) => {
const sp = Math.round(spi * 10000) / 10000;

const renderedColor = new Map();
const CoordsOfInterest = new Map();

for (const [id, segment] of LineSegments.entries()) {
let linewidth = sp,
spacing = linewidth + sp,
maxline = segment.colors.length;

const LineBuffer = _Imports.Geospatial.turf.buffer(
_Imports.Geospatial.turf.lineString(segment.geometry),
maxline * spacing - ((maxline - 1) * spacing) / 2 + 0.001
);

for (const color of segment.colors) {
const lineoffset =
segment.colors.indexOf(color) * spacing -
((maxline - 1) * spacing) / 2;

const line = _Imports.Geospatial.turf.lineString(segment.geometry, {
stroke: color,
id: `${id}:${color}`
});

const ClippedLine = ClipLine(line, Math.max(0.005, sp * 3));

const offsetClippedLine = _Imports.Geospatial.turf.lineOffset(
ClippedLine,
Math.floor(lineoffset * 10000) / 10000
);

const PointsThatFallInLine =
offsetClippedLine.geometry.coordinates.filter((c) => {
return _Imports.Geospatial.turf.booleanPointInPolygon(
_Imports.Geospatial.turf.point(c),
LineBuffer
);
});

CoordsOfInterest.set(`${id}:${color}:top`, PointsThatFallInLine[0]);
CoordsOfInterest.set(
`${id}:${color}:bottom`,
PointsThatFallInLine[PointsThatFallInLine.length - 1]
);

if (renderedColor.has(color)) {
renderedColor.get(color).push(PointsThatFallInLine);
} else {
renderedColor.set(color, [PointsThatFallInLine]);
}
}
}

for (const connection of LineConnections) {
const coords = [
CoordsOfInterest.get(
`Segment:${connection.from[0]}:${connection.color}:${connection.from[1]}`
),
CoordsOfInterest.get(
`Segment:${connection.to[0]}:${connection.color}:${connection.to[1]}`
)
];

if (!coords.includes(undefined)) {
const line = _Imports.Geospatial.turf.lineString(coords, {
stroke: connection.color,
id: `Connection:${connection.from[0]}:${connection.from[1]}:${connection.to[0]}:${connection.to[1]}:${connection.color}`
});

renderedColor.get(connection.color).push(line.geometry.coordinates);
}
}

const rendered = [];

for (const [color, coords] of renderedColor.entries()) {
rendered.push(
_Imports.Geospatial.turf.multiLineString(coords, {
id: color,
stroke: color
})
);
}

return _Imports.Geospatial.turf.featureCollection(rendered);
};
}
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