Closed mholt closed 1 year ago
Hi @mholt,
You can also use the "Highlight features within a bounding box" example as a reference. As far as I understand your use case, you'd only need to unproject the bbox
from pixel coordinates to the geographical coordinates like this:
const latLng1 = map.unproject(bbox[0]);
const latLng2 = map.unproject(bbox[1]);
@stepankuzmin Thank you! I had actually found that example and used it as a reference. I didn't know about unproject()
at the time though, so I reworked that example to use a map event instead of a canvas event, along these lines:
on('click', '#bbox-toggle', event => {
if ($('#bbox-toggle').classList.contains('active')) {
// disable panning and box zooming (so as not to conflict with our custom box drawing)
map.boxZoom.disable();
map.dragPan.disable();
// show useful cursor indicative of creating a box
$('.mapboxgl-canvas-container').style.cursor = 'crosshair';
// when user clicks down on map, start drawing process
map.on('mousedown', mouseDown);
const canvas = map.getCanvasContainer();
// Variable to hold the starting xy point and
// coordinates when mousedown occured.
let startPt, startCoord;
// Variable for the draw box element.
let box;
function mouseDown(e) {
map.on('mousemove', onMouseMove);
map.on('mouseup', onMouseUp);
// for cancellation
document.addEventListener('keydown', onKeyDown);
// Capture the first xy coordinates
startPt = e.point;
startCoord = e.lngLat;
}
function onMouseMove(e) {
// Capture the ongoing xy coordinates
let current = e.point;
if (!box) {
box = document.createElement('div');
box.classList.add('boxdraw');
canvas.appendChild(box);
}
const minX = Math.min(startPt.x, current.x),
maxX = Math.max(startPt.x, current.x),
minY = Math.min(startPt.y, current.y),
maxY = Math.max(startPt.y, current.y);
// Adjust width and xy position of the box element ongoing
const pos = `translate(${minX}px, ${minY}px)`;
box.style.transform = pos;
box.style.width = maxX - minX + 'px';
box.style.height = maxY - minY + 'px';
}
function onMouseUp(e) {
finish([startCoord, e.lngLat]);
}
function onKeyDown(e) {
if (e.keyCode === 27) finish();
}
function finish(bbox) {
// remove the box polygon (literally a "poly-gone", ha...)
if (box) {
box.remove();
box = null;
}
if (bbox) {
const bboxEl = $('#bbox');
bboxEl.value = `(${bbox[0].lat.toFixed(4)}, ${bbox[0].lng.toFixed(4)}) (${bbox[1].lat.toFixed(4)}, ${bbox[1].lat.toFixed(4)})`
bboxEl.dataset.lat1 = bbox[0].lat;
bboxEl.dataset.lon1 = bbox[0].lng;
bboxEl.dataset.lat2 = bbox[1].lat;
bboxEl.dataset.lon2 = bbox[1].lng;
trigger('#bbox', 'change');
}
map.dragPan.enable();
map.boxZoom.enable();
map.off('mousedown', mouseDown);
map.off('mousemove', onMouseMove);
map.off('mouseup', onMouseUp);
$('.mapboxgl-canvas-container').style.cursor = '';
$('#bbox-toggle').classList.remove('active');
}
}
});
(in case it helps anyone else who wants to do this in the future)
My code is based on the example you linked to.
My page has a #bbox-toggle
element that, when active, should allow the user to draw a box (instead of pan). I use map events to get the geo coordinates instead of using canvas events which only have x,y coordinates (because I didn't know about unproject
). My code seems to work, but maybe unproject
will make things simpler!
Thanks again for the help. Mapbox is pretty cool.
Motivation
I'm trying to implement a "bounding box search" feature: the user can click+drag a box over the map to find results within that box.
Conveniently, Mapbox GL JS already has a function to allow the user to draw a box. It even zooms into the selected area!
Relevant events:
boxzoomstart
: https://docs.mapbox.com/mapbox-gl-js/api/map/#map.event:boxzoomstartboxzoomend
: https://docs.mapbox.com/mapbox-gl-js/api/map/#map.event:boxzoomendDesign Alternatives
Currently, doing this requires a tedious amount of code to wrangle event handling, drawing a polygon, and setting the cursor.
All I really need are the 4 sides of the bounding box in lat/lon (max+min latitude, max+min longitude), after the box is created.
Design
Add a
coords
property to theMapBoxZoomEvent
object with the 4 sides of the box.Mock-Up
^ From that, I can perform a search in my application and display relevant results on the map.
Of course, since the primary purpose of this function is to zoom the viewbox, my application has a button to toggle the "create bounding box" state. If the user clicks the button, then draws a box, we use that box as a search parameter and deactivate the button state. This solves the problem of the user unintentionally revising their search when they mean only to zoom.
Concepts
Uses only existing concepts as far as I can tell. Just providing the coordinates through the event.
Implementation
When the event is generated, simply add
coords
to the event object's properties.Thank you for considering!