/**
* BeNomad BeMap JavaScript API - OpenLayers v10 - Draw Interaction
*/
bemap.OlMap.prototype.drawPolygon = function(options, callback) {
return this._startDraw('Polygon', options, callback);
};
bemap.OlMap.prototype.drawPolyline = function(options, callback) {
return this._startDraw('LineString', options, callback);
};
bemap.OlMap.prototype.drawCircle = function(options, callback) {
return this._startDraw('Circle', options, callback);
};
bemap.OlMap.prototype.drawRectangle = function(options, callback) {
return this._startDraw('Circle', options, callback, true); // geometryFunction makes it a box
};
bemap.OlMap.prototype.drawMarker = function(options, callback) {
return this._startDraw('Point', options, callback);
};
bemap.OlMap.prototype._startDraw = function(type, options, callback, isBox) {
if (typeof ol === 'undefined' || !ol.interaction || !ol.interaction.Draw) {
console.warn('ol.interaction.Draw not available');
return new bemap.Listener();
}
var opts = options || {};
var _this = this;
// Cancel any existing draw
this.cancelDraw();
// Create a temporary vector source for drawing
var drawSource = new ol.source.Vector({ features: [] });
var drawLayer = new ol.layer.Vector({ source: drawSource });
this.native.addLayer(drawLayer);
// Build draw interaction options
var drawOpts = { type: type, source: drawSource };
if (isBox) {
drawOpts.type = 'Circle';
if (ol.interaction.Draw.createBox) drawOpts.geometryFunction = ol.interaction.Draw.createBox();
}
var interaction = new ol.interaction.Draw(drawOpts);
interaction.on('drawend', function(evt) {
var feature = evt.feature;
var geom = feature.getGeometry();
// Transform back to WGS84
var cloned = geom.clone().transform(_this.native.getView().getProjection(), bemap.Map.PROJ.EPSG_WGS84);
var defaultPolyStyle = opts.style || new bemap.PolygonStyle({
fillColor: new bemap.Color(0, 102, 204, 0.2),
borderColor: new bemap.Color(0, 102, 204, 1),
borderWidth: 2
});
var bemapObj = null;
if (type === 'Polygon' || isBox) {
var ringCoords = cloned.getCoordinates()[0];
var bemapCoords = [];
for (var i = 0; i < ringCoords.length - 1; i++) { // skip closing coord
bemapCoords.push(new bemap.Coordinate(ringCoords[i][0], ringCoords[i][1]));
}
bemapObj = new bemap.Polygon(bemapCoords, { style: defaultPolyStyle });
} else if (type === 'LineString') {
var lineCoords = cloned.getCoordinates();
var bemapLineCoords = [];
for (var j = 0; j < lineCoords.length; j++) {
bemapLineCoords.push(new bemap.Coordinate(lineCoords[j][0], lineCoords[j][1]));
}
var defaultLineStyle = opts.style || new bemap.LineStyle({ color: new bemap.Color(230, 57, 70, 1), width: 3 });
bemapObj = new bemap.Polyline(bemapLineCoords, { style: defaultLineStyle });
} else if (type === 'Circle') {
var center = cloned.getCenter();
var centerCoord = new bemap.Coordinate(center[0], center[1]);
// Compute real-Earth radius in meters via Haversine from center to edge point
var mercCenter = geom.getCenter();
var mercRadius = geom.getRadius();
var edgeMerc = [mercCenter[0] + mercRadius, mercCenter[1]];
var edgeWgs = ol.proj.transform(edgeMerc, _this.native.getView().getProjection(), bemap.Map.PROJ.EPSG_WGS84);
var R = 6371000;
var lat1 = center[1] * Math.PI / 180, lat2 = edgeWgs[1] * Math.PI / 180;
var dLat = (edgeWgs[1] - center[1]) * Math.PI / 180;
var dLon = (edgeWgs[0] - center[0]) * Math.PI / 180;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon/2) * Math.sin(dLon/2);
var radiusM = R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var circleStyle = opts.style || new bemap.CircleStyle({
fillColor: new bemap.Color(0, 102, 204, 0.2),
borderColor: new bemap.Color(0, 102, 204, 1),
borderWidth: 2
});
bemapObj = new bemap.Circle(centerCoord, radiusM, { style: circleStyle });
} else if (type === 'Point') {
var pt = cloned.getCoordinates();
bemapObj = new bemap.Marker(new bemap.Coordinate(pt[0], pt[1]));
}
// Clean up
_this.native.removeInteraction(interaction);
_this.native.removeLayer(drawLayer);
_this._drawInteraction = null;
if (bemapObj) {
if (callback) {
callback(new bemap.MapEvent({
map: _this,
bemapObject: bemapObj,
coordinate: bemapObj.coordinate || (bemapObj.getCoordinates ? bemapObj.getCoordinates()[0] : null)
}));
}
// Auto-add to map
if (opts.addToMap !== false) {
if (bemap.inheritsof(bemapObj, bemap.Polygon)) _this.addPolygon(bemapObj);
else if (bemap.inheritsof(bemapObj, bemap.Polyline)) _this.addPolyline(bemapObj);
else if (bemap.inheritsof(bemapObj, bemap.Circle)) _this.addCircle(bemapObj);
else if (bemap.inheritsof(bemapObj, bemap.Marker)) _this.addMarker(bemapObj);
}
// Auto-enable vertex editing
if (opts.editable !== false && bemap.inheritsof(bemapObj, bemap.Polygon)) {
_this.editPolygon(bemapObj, callback);
}
}
});
this.native.addInteraction(interaction);
this._drawInteraction = interaction;
this._drawLayer = drawLayer;
// Set crosshair cursor
var container = document.getElementById(this.target);
if (container) container.classList.add('bemap-drawing-active');
return new bemap.Listener({ native: interaction, bemapObject: null, key: 'draw' });
};
bemap.OlMap.prototype.cancelDraw = function() {
if (this._drawInteraction) {
this.native.removeInteraction(this._drawInteraction);
this._drawInteraction = null;
}
if (this._editInteraction) {
this.native.removeInteraction(this._editInteraction);
this._editInteraction = null;
}
if (this._drawLayer) {
this.native.removeLayer(this._drawLayer);
this._drawLayer = null;
}
var container = document.getElementById(this.target);
if (container) container.classList.remove('bemap-drawing-active');
return this;
};
bemap.OlMap.prototype.editPolygon = function(polygon, callback) {
if (!polygon || !polygon.native) return new bemap.Listener();
if (typeof ol === 'undefined' || !ol.interaction || !ol.interaction.Modify) return new bemap.Listener();
var _this = this;
// Remove previous edit interaction if any
if (this._editInteraction) {
this.native.removeInteraction(this._editInteraction);
this._editInteraction = null;
}
var modify = new ol.interaction.Modify({
features: new ol.Collection([polygon.native])
});
var MERGE_PX = 15;
modify.on('modifyend', function() {
var geom = polygon.native.getGeometry().clone().transform(
_this.native.getView().getProjection(), bemap.Map.PROJ.EPSG_WGS84
);
var ringCoords = geom.getCoordinates()[0];
var updatedCoords = [];
for (var i = 0; i < ringCoords.length - 1; i++) {
updatedCoords.push(new bemap.Coordinate(ringCoords[i][0], ringCoords[i][1]));
}
// Merge: remove near-duplicate vertices
if (updatedCoords.length > 3) {
var merged = [updatedCoords[0]];
for (var m = 1; m < updatedCoords.length; m++) {
var pxA = _this.native.getPixelFromCoordinate(ol.proj.transform(
[updatedCoords[m].getLon(), updatedCoords[m].getLat()], bemap.Map.PROJ.EPSG_WGS84, _this.native.getView().getProjection()
));
var tooClose = false;
for (var n = 0; n < merged.length; n++) {
var pxB = _this.native.getPixelFromCoordinate(ol.proj.transform(
[merged[n].getLon(), merged[n].getLat()], bemap.Map.PROJ.EPSG_WGS84, _this.native.getView().getProjection()
));
if (pxA && pxB && Math.sqrt(Math.pow(pxA[0] - pxB[0], 2) + Math.pow(pxA[1] - pxB[1], 2)) < MERGE_PX) {
tooClose = true; break;
}
}
if (!tooClose) merged.push(updatedCoords[m]);
}
updatedCoords = merged;
}
polygon.coords = updatedCoords;
if (callback) {
callback(new bemap.MapEvent({ map: _this, bemapObject: polygon }));
}
});
// Right-click to delete nearest vertex
var contextHandler = function(evt) {
evt.preventDefault();
var geom = polygon.native.getGeometry();
var ringCoords = geom.getCoordinates()[0];
if (ringCoords.length - 1 <= 3) return; // min 3 vertices (ring has closing coord)
var pixel = _this.native.getEventPixel(evt);
var coord = _this.native.getCoordinateFromPixel(pixel);
var bestIdx = -1, bestDist = 20; // max 20px
for (var v = 0; v < ringCoords.length - 1; v++) {
var vPx = _this.native.getPixelFromCoordinate(ringCoords[v]);
if (vPx) {
var d = Math.sqrt(Math.pow(pixel[0] - vPx[0], 2) + Math.pow(pixel[1] - vPx[1], 2));
if (d < bestDist) { bestDist = d; bestIdx = v; }
}
}
if (bestIdx >= 0) {
ringCoords.splice(bestIdx, 1);
ringCoords[ringCoords.length - 1] = ringCoords[0]; // re-close ring
geom.setCoordinates([ringCoords]);
var wgs = geom.clone().transform(_this.native.getView().getProjection(), bemap.Map.PROJ.EPSG_WGS84).getCoordinates()[0];
var newCoords = [];
for (var nc = 0; nc < wgs.length - 1; nc++) {
newCoords.push(new bemap.Coordinate(wgs[nc][0], wgs[nc][1]));
}
polygon.coords = newCoords;
if (callback) callback(new bemap.MapEvent({ map: _this, bemapObject: polygon }));
}
};
var mapEl = document.getElementById(_this.target);
if (mapEl) mapEl.addEventListener('contextmenu', contextHandler);
polygon._contextHandler = contextHandler;
this.native.addInteraction(modify);
this._editInteraction = modify;
return new bemap.Listener({ native: modify, bemapObject: polygon, key: 'editPolygon' });
};