Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save lancegliser/7031e7e3a2e5ce47926c540dc1a1c4ed to your computer and use it in GitHub Desktop.
Save lancegliser/7031e7e3a2e5ce47926c540dc1a1c4ed to your computer and use it in GitHub Desktop.
Provides an example for drawing curved lines from nodes using turf.js' bezier functionality including line styling with gradients, transparency, and zoom customization. Oh and popups.
import mapboxgl, {
getBezierLine, getDarkenedColor,
getTransparency,
primaryColor, setMapCursorPointer, unsetMapCursorPointer
} from "../../../../../Projects/Demo/frontend/src/utils/mapbox/mapbox";
import {EventData, MapboxGeoJSONFeature, MapMouseEvent} from "mapbox-gl";
import {Properties} from "@turf/helpers";
import midpoint from "@turf/midpoint";
import * as turfHelpers from "@turf/helpers";
import bezierSpline from "@turf/bezier-spline";
const drawLines = (map: mapboxgl.Map, nodes: Node[]) => {
nodes
.filter(
(relation) =>
relation.longitude &&
relation.latitude &&
relation.from?.latitude &&
relation.from?.longitude
)
.forEach((relation, index) => {
const originNode = relation.from!;
const originCoordinates: [number, number] = [
originNode.longitude!,
originNode.latitude!,
];
const destinationCoordinates: [number, number] = [
relation.longitude!,
relation.latitude!,
];
const line = getBezierLine(originCoordinates, destinationCoordinates, {
resolution: 3000,
});
if (!line) {
return;
}
line.properties = line.properties || {};
line.properties.popupContent = `
<div class="font-weight-bold">${relation.from?.displayName} -> ${relation.name}</div>
<div>${relation.address}</div>
`;
const featureCollection: GeoJSON.FeatureCollection<GeoJSON.LineString> = {
type: "FeatureCollection",
features: [line],
};
requestAnimationFrame(() => {
renderLine(featureCollection, relation.relationDegree, index);
});
});
const renderLine = (
featureCollection: GeoJSON.FeatureCollection<GeoJSON.LineString>,
relationDegree: number,
index: number
) => {
const lineId = `bezierFeatures-${index}`;
map.addSource(lineId, {
type: "geojson",
lineMetrics: true,
data: featureCollection,
});
state.lineSourcesIds.push(lineId);
// A potential hook for changing line colors based on data points.
const color = primaryColor;
const minimumLineWidth = relationDegree == 1 ? 2 : 1;
map.addLayer({
id: lineId,
source: lineId,
type: "line",
paint: {
"line-width": [
"interpolate",
["linear"],
["zoom"],
1,
["*", minimumLineWidth, 1],
15,
["*", minimumLineWidth, 4],
],
"line-gradient": [
"interpolate",
["linear"],
["line-progress"],
0,
getTransparency(color, relationDegree == 1 ? 1 : 0.75),
1,
getTransparency(
getDarkenedColor(color),
relationDegree == 1 ? 0.75 : 0.5
),
],
},
});
state.lineLayerIds.push(lineId);
map.on("click", lineId, onLineClick);
map.on("mouseenter", lineId, setMapCursorPointer);
map.on("mouseleave", lineId, unsetMapCursorPointer);
};
};
const getBezierLine = <P = Properties>(
origin: [number, number],
destination: [number, number],
options?: {
properties?: P;
resolution?: number;
sharpness?: number;
}
): GeoJSON.Feature<GeoJSON.LineString> | undefined => {
const [originLongitude, originLatitude] = origin;
const [destinationLongitude, destinationLatitude] = destination;
const apex = midpoint(
[originLongitude, originLatitude],
[destinationLongitude, destinationLatitude]
);
if (!apex.geometry) {
return;
}
const line = turfHelpers.lineString([
origin,
apex.geometry.coordinates,
destination,
]);
return bezierSpline(line, options);
};
const onLineClick = (
event: MapMouseEvent & {
features?: MapboxGeoJSONFeature[] | undefined;
} & EventData
) => {
if (!event.features) {
return;
}
const content = event.features[0].properties?.popupContent;
if (!content) {
return;
}
const coordinates = event.lngLat;
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(content)
.addTo(event.target);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment