When zoomed, the map invokes zoomstart, zoom and zoomend events. Is there a way to tell if the map is zooming in our out? Really at zoomend, I need to understand if the map was zoomed in our out.
The following code accomplishes this...
var currentZoomLevel;
map.events.add('zoomstart', layer, function (e) { currentZoomLevel = map.getCamera().zoom; })
map.events.add('zoomend', layer, function (e) {
if (currentZoomLevel > map.getCamera().zoom) {
// zoomed out
}
})
Related
I'm using mapbox and turf for a project. When I click on a country I want it to go to that country and display the whole country on the screen. So if I'm zoomed into a small country like Bhutan and I click on China I want it to zoom out and go up to show all of the country.
When I am zoomed out I can accomplish this and click any country and it will properly fitBounds but if I am already zoomed in it just moves slightly and doesn't zoom out. Here's the code
import mapboxgl from '!mapbox-gl'; // eslint-disable-line import/no-webpack-loader-syntax
import 'mapbox-gl/dist/mapbox-gl.css';
import bbox from '#turf/bbox';
...
map.current.on('click', 'countries-join', (e) => {
const clickedPoint = [[e.point.x, e.point.y], [e.point.x, e.point.y]];
const selectedFeatures = map.current.queryRenderedFeatures(clickedPoint, {layers['countries-join']});
map.current.zoomTo(2) // I added this so it might zoom out before fittingBounds. It does not
var bb = bbox({ type: 'FeatureCollection', features: selectedFeatures });
map.current.fitBounds(bb, {padding: 50, linear: false});
});
If I console log the selectedFeatures when I'm zoomed out vs zoomed in it actually returns less geometry. Can anyone assist?
I am attempting to get the elevation of certain coordinates on the map using mapbox.
Based on the documentation, i can use queryTerrainElevation.
Sample Code:
map.on("click", (data) => {
console.log(data);
const elevation = map?.queryTerrainElevation(data.lngLat, {
exaggerated: true,
});
console.log("Elevation: " + elevation)
});
Console logs:
Using the mapbox tilequery with the same coordinates:
https://api.mapbox.com/v4/mapbox.mapbox-terrain-v2/tilequery/95.9345,41.2565.json?access_token=<mapbox_token>
There is an elevation value in the response:
You must be adding the custom layer after just loading the style. So, the terrain value isn't updated at that time. That's why you get null.
Do it like this and it should work.
map.on('idle', () => {
const elevation = map.queryTerrainElevation(coordinates, {exaggerated: false});
})
This runs the layer code after the map is loaded. Instead of just the style.
I'm using this for a 360 image and I need the camera to stay fixed at (0,0,0)
If I update the camera position the controls stop working.
I've seen this post https://codeworkshop.dev/blog/2020-04-03-adding-orbit-controls-to-react-three-fiber/ which kind of has a fix but seems out of date, the extend functionality doesn't make orbitControls available.
I've tried various combinations of
const onChange = () => {
camera.position.set(0, 0, 0);
ref.current.update();
};
Or
useEffect(() => {
if (ref.current && scene.projection === SceneProjection['3603D']) {
camera.position.set(0, 0, 0);
ref.current.update();
}
}, [ref, scene]);
Is there a simple way to lock the camera position with OrbitControls and just rotate the camera?
I think you might be able to set controls.maxDistance to something really small, like 0.01. That way it only rotates around its origin by an imperceptible amount.
You can read more about the .maxDistance attribute in the docs](https://threejs.org/docs/index.html?#examples/en/controls/OrbitControls.maxDistance)
I am using google-maps-react module and everything until the last stretch is fine. Using this package, I am able to click on map, set multiple markers and define my route "visually". I did not think that Polygon would not take actual streets and map geometry into consideration (stupid me). Could someone help me out and suggest on how to provide properly drawn routes on map, instead of straight lines connecting marker X to marker Y? This is what I have so far visually:
And this is the coordinates array that I am forming in my application and drawing polygon by:
I am using Google maps api and google-maps-react package.
As was correctly mentioned in comment, Directions API needs to be utilized for that purpose:
Directions are displayed as a polyline drawing the route on a map, or
additionally as a series of textual description within a element
(for example, "Turn right onto the Williamsburg Bridge ramp")
The following example demonstrates how to integrate Google Maps API Directions Service into google-maps-react to display a route.
It is assumed data prop contains coordinates represented in format
specified in provided question. Directions Service code has been adapted
from this example
Example
class MapContainer extends React.Component {
constructor(props) {
super(props);
this.handleMapReady = this.handleMapReady.bind(this);
}
handleMapReady(mapProps, map) {
this.calculateAndDisplayRoute(map);
}
calculateAndDisplayRoute(map) {
const directionsService = new google.maps.DirectionsService();
const directionsDisplay = new google.maps.DirectionsRenderer();
directionsDisplay.setMap(map);
const waypoints = this.props.data.map(item =>{
return{
location: {lat: item.lat, lng:item.lng},
stopover: true
}
})
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
directionsService.route({
origin: origin,
destination: destination,
waypoints: waypoints,
travelMode: 'DRIVING'
}, (response, status) => {
if (status === 'OK') {
directionsDisplay.setDirections(response);
} else {
window.alert('Directions request failed due to ' + status);
}
});
}
render() {
return (
<div className="map-container">
<Map
google={this.props.google}
className={"map"}
zoom={this.props.zoom}
initialCenter={this.props.center}
onReady={this.handleMapReady}
/>
</div>
);
}
}
Demo
You might want to check out this page
https://developers.google.com/maps/documentation/roads/snap
The response from that API call with the data you've already gotten from the lines drawn should give you the JSON data you need to map your path to the roads. This might not be the most elegant solution seeing as you might need to add a button or something to calculate the route to roads or something similar.
Alternatively you might be able to send out the API call after you have two points and have the map update after every line segment is placed. That would require a lot of API calls though. Hope that helps!
What I want to achieve:
Have a <Map><FeatureGroup><Circle />[1 or more]...</FeatureGroup></Map> hierarchy and fit the map bounds to the feature group so that all the circles are in the viewport.
If there is only one circle, it should fit the bounds (ie: zoom in on) to that circle.
What I've tried:
giving FeatureGroup a ref and calling getBounds on it to pass onto Map. Because of the lifecycle FeatureGroup doesn't exist at the time componentDidMount is called - it gets rendered later (https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-161594328).
Storing Circle in state and calling getBounds on that (assuming, in this case, that there is only one circle. That didn't work either.
I think I might need to do something with the React Context but I'm not sure that I fully understand it right now, so I need some help.
Other information
I'm using react-leaflet#2.1.2
Thanks for any help offered!
Because the contents of the Map are unavailable at componentDidMount-time (https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-161594328) you cannot get the bounds of the FeatureGroup at that point, and out of all the refs you assign, only the Map ref will be available in this.refs.
However, as per this GitHub comment: https://github.com/PaulLeCam/react-leaflet/issues/106#issuecomment-366263225 you can give a FeatureGroup an onAdd handler function:
<FeatureGroup ref="features" onAdd={this.onFeatureGroupAdd}>...
and you can then use the Map refs to access the leafletElement and call fitBounds with the bounds of the incoming event target, which will be the FeatureGroup:
onFeatureGroupAdd = (e) => {
this.refs.map.leafletElement.fitBounds(e.target.getBounds());
}
This will then "zoom" the map into the bounds of your FeatureGroup, as desired.
Update
I modified my React component so that zoom and centre are controlled by query parameters. The problem with the above solution was that if you zoomed in on a MarkerClusterGroup by clicking on it, for example, it would update the zoom in the url, re-render the map and re-call onFeatureGroupAdd, thus undoing all the marker cluster goodness.
What I needed was to access the zoom level required to keep the newly drawn circle nicely in bounds, then update the url with the correct zoom level and center.
onDrawCircle = (e) => {
...
var targetZoom = this.refs.map.leafletElement.getBoundsZoom(e.layer.getBounds());
// Call function to update url here:
functionToUpdateUrl(targetZoom, e.layer.getBounds().getCenter());
}
}
In order to be able to control the whole map I also call functionToUpdateUrl in onZoomEnd and onDragEnd event handlers, like so:
onChangeView = (e) => {
functionToUpdateUrl(e.target._zoom, this.refs.map.leafletElement.getCenter());
}
and one for handling cluster clicks:
onClusterClick = (e) => {
// This time we want the center of the layer, not the map?
functionToUpdateUrl(e.target._zoom, (e.layer ? e.layer.getBounds().getCenter() : e.target.getBounds().getCenter()));
}
Then, when rendering the Map element, pass these properties:
<Map
center={center}
ref='map'
zoom={zoom}
maxZoom={18}
onZoomEnd={this.onChangeView}
onDragEnd={this.onChangeView}
>
....
</Map>
And remember to give any MarkerClusterGroups their onClusterClick callback:
<MarkerClusterGroup onAdd={this.onMarkerGroupAdd} onClusterClick={this.onClusterClick}>
Have you tried doing getBounds in the componentDidMount function instead of componentWillMount? If that doesn't work then I'd suggest extending the FeatureGroup component and adding an onLoaded function as as prop and call that function in the componentDidMount function of your extended component. And by extending the FeatureGroup component I actually mean copying/pasting it from here. (if you care about why you need to copy that whole file check this thread)
This isn't tested but your code will probably look something like
import { FeatureGroup } from 'leaflet';
import { withLeaflet, Path } from 'react-leaflet';
class CustomFeatureGroup extends Path {
createLeafletElement(prop) {
const el = new FeatureGroup(this.getOptions(props));
this.contextValue = {
...props.leaflet,
layerContainer: el,
popupContainer: el,
};
return el;
}
componentDidMount() {
super.componentDidMount();
this.setStyle(this.props);
/*
Here you can do your centering logic with an onLoad callback or just
by using this.leafletElement.map or whatever
*/
this.props.onLoaded();
}
}
export default withLeaflet(CustomFeatureGroup)
Note: If you are using react-leaflet V1 this is actually way easier and I can edit this answer with that code if needed.