/**
* BeNomad BeMap JavaScript API - MapLibre v5 - Polyline
*/
bemap.MapLibreMap.prototype.addPolyline = function(polyline, options) {
if (!polyline || !bemap.inheritsof(polyline, bemap.Polyline)) return this;
var opts = options || {};
this._addOwnToProperties(polyline);
var id = polyline._bemapId;
polyline._maplibreSourceId = id + '-src';
polyline._maplibreLayerId = id + '-layer';
var coords = [];
var bemapCoords = polyline.getCoordinates();
for (var i = 0; i < bemapCoords.length; i++) {
coords.push([bemapCoords[i].getLon(), bemapCoords[i].getLat()]);
}
var lineColor = 'rgba(0,0,0,1)', lineWidth = 3, lineDash = [];
if (polyline.style) {
if (polyline.style.color) lineColor = this._colorToRgba(polyline.style.color);
if (polyline.style.width) lineWidth = polyline.style.width;
if (polyline.style.type === bemap.LineStyle.TYPE.DASH) lineDash = [10, 5];
else if (polyline.style.type === bemap.LineStyle.TYPE.DOT) lineDash = [2, 5];
else if (polyline.style.type === bemap.LineStyle.TYPE.DOT_DASH) lineDash = [2, 5, 10, 5];
}
var props = { _bemapId: polyline._bemapId };
if (polyline.properties) { for (var k in polyline.properties) { props[k] = polyline.properties[k]; } }
var geojson = {
type: 'Feature',
geometry: { type: 'LineString', coordinates: coords },
properties: props
};
this._geoJsonData[polyline._maplibreSourceId] = geojson;
// Resolve the target layer FIRST and record the claim SYNCHRONOUSLY (push
// to geoJsonIds + features). The cancellation check inside addToMap
// reads geoJsonIds — if we pushed AFTER scheduling, a sync-fired
// addToMap would see an empty array and abort.
polyline.native = { sourceId: polyline._maplibreSourceId, layerId: polyline._maplibreLayerId };
if (polyline.map === null) polyline.map = this;
var l = opts.layer && bemap.inheritsof(opts.layer, bemap.Layer) ? opts.layer : this.getLayerByName(bemap.Map.DEFAULT_LAYER.POLYLINE);
if (polyline.layer === null) polyline.layer = l;
if (l && l.native && l.native.geoJsonIds) l.native.geoJsonIds.push(polyline._maplibreLayerId);
if (l && l.native && l.native.features) l.native.features.push(polyline);
var _this = this;
var _added = false;
var addToMap = function() {
if (_added) return;
// CANCELLATION CHECK: if the polyline was added during a style-load
// gap (e.g. just after a 401-triggered setStyle), `addToMap` was
// queued on _whenStyleReady. If `clearLayer` ran between the
// schedule and the actual fire — which is what happens when evmove5
// re-renders routes — the layer's geoJsonIds array was reset and the
// polyline was logically cleared. Without this guard, the queued
// addToMap still fires later, adds source+layer to MapLibre's style,
// and produces an ORPHAN that no future clearLayer can find (the
// layer's tracking doesn't claim it). Symptom: "first 5 polylines
// stay on top in true colour after route swap".
if (l && l.native && l.native.geoJsonIds &&
l.native.geoJsonIds.indexOf(polyline._maplibreLayerId) === -1) {
return;
}
try {
if (!_this.native.getSource(polyline._maplibreSourceId)) {
_this.native.addSource(polyline._maplibreSourceId, { type: 'geojson', data: geojson });
}
if (!_this.native.getLayer(polyline._maplibreLayerId)) {
var layerDef = {
id: polyline._maplibreLayerId,
type: 'line',
source: polyline._maplibreSourceId,
paint: { 'line-color': lineColor, 'line-width': lineWidth, 'line-opacity': 1 },
layout: { 'line-cap': 'round', 'line-join': 'round' }
};
if (lineDash.length > 0) layerDef.paint['line-dasharray'] = lineDash;
_this.native.addLayer(layerDef);
if (_this._geoJsonLayerIds.indexOf(polyline._maplibreLayerId) === -1) {
_this._geoJsonLayerIds.push(polyline._maplibreLayerId);
}
}
_added = true;
} catch(e) {
if (typeof console !== 'undefined' && console.warn) {
console.warn('[bemap.MapLibreMap.addPolyline] add failed for ' +
polyline._maplibreLayerId + ': ' + (e && e.message ? e.message : e));
}
}
};
// Schedule via _enqueueStyleOp so a burst of addPolyline calls (e.g. an
// EV-routing re-render after route swap, 17+ polylines per click) coalesces
// into ONE MapLibre render pass per rAF tick instead of 17. Eliminates the
// "blank flicker" between clearLayer and the first new polyline.
this._enqueueStyleOp(polyline._maplibreLayerId, addToMap);
return this;
};
bemap.MapLibreMap.prototype.removePolyline = function(polyline) {
if (polyline && polyline.native) {
try {
if (this.native.getLayer(polyline._maplibreLayerId)) this.native.removeLayer(polyline._maplibreLayerId);
if (this.native.getSource(polyline._maplibreSourceId)) this.native.removeSource(polyline._maplibreSourceId);
var idx = this._geoJsonLayerIds.indexOf(polyline._maplibreLayerId);
if (idx > -1) this._geoJsonLayerIds.splice(idx, 1);
} catch(e) {}
if (polyline._bemapId) delete this._featureRegistry[polyline._bemapId];
if (polyline._maplibreSourceId) delete this._geoJsonData[polyline._maplibreSourceId];
polyline.map = null;
polyline.layer = null;
}
return this;
};
bemap.MapLibreMap.prototype.draggablePolyline = function(polyline, callback, options) {
return this._draggableFeature(polyline, callback, options, { singleFeature: true });
};
bemap.MapLibreMap.prototype.draggablePolylines = function(callback, options) {
return this._draggableFeature(null, callback, options, { polylines: true });
};