Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
is it possible to use leaflet to move tile layer based on a offset
The problem is that I use OSM data to display markers and building outlines on the map but since am using another Tile Provider the OSM data is not sync with satellite image
Is there a simple way of adding offset in leaflet?
attached a screenshoot from openstreetmap's iD editor :
https://youtu.be/IRLyMKVERvo
Is it possible to use leaflet to move tile layer based on a offset?
Yes.
Is there a simple way of adding offset in leaflet?
No.
There are several approaches to offsetting a Leaflet L.TileLayer, but they're not straight forward. I would advise reading about extending Leaflet classes and reading Leaflet's source code before undertaking such a task.
One approach would be to use a decorator pattern on the specific bit of code that calculates the CSS offset of tiles, i.e. the internal _getTilePos() method of L.GridLayer:
_getTilePos: function (coords) {
return coords.scaleBy(this.getTileSize()).subtract(this._level.origin);
},
That internal method takes in the tile coordinates, and returns the pixel coordinates relative to what Leaflet calls the origin pixel. Those concepts are documented.
So I can create a decorator by subclassing L.TileLayer and creating a new class with a new implementation for _getTilePos (but reusing the implementation of the parent class), e.g.
var OffsetTileLayer = L.TileLayer.extend({
_getTilePos: function (coords) {
var pos = L.TileLayer.prototype._getTilePos.call(this, coords);
return pos.add([25, 25]);
},
});
var offsetTileLayer = new OffsetTileLayer(/* etc */);
offsetTileLayer.addTo(map);
See a working example.
That will offset the CSS position of tiles (which is represented by an instance of L.Point) by 25 horizontal and vertical pixels. Note that the offset is CSS pixels; not meters and not degrees of latitude/longitude.
It would be possible to make that offset depend on the tile coordinates (from which the latitude, longitude and scale can be derived), but I suspect that there would be artefacts (such as gaps between tiles) if the geodetic calculations involved (i.e. how to calculate meters from the tile coords) are not done with care.
Related
This question already has an answer here:
Invert Y axis of L:CRS.Simple map on Vue2-Leaflet
(1 answer)
Closed last year.
I'm trying to implement Leaflet for a game map. It kinda works with L.CRS.Simple. However, in the game, the "southwest" corner should be [0,0], while the "northeast" corner should be [10000,10000] (it's a square map).
How do I tell Leaflet to use this as a coordinate system?
Try to change Leaflet's CRS transform like this:
L.CRS.Simple.transformation = L.transformation(scale_x, shift_x, scale_y, shift_y);
scale_x, scale_y in my case is 1, you may need change scale_y to -1.
I am new to leaflet and I want the restrict the panning of a world map horizontally and not vertically (longitude but not latitude) because this map will display pictures when I click on them and I cant see well the image when i restrict the panning horizontally AND vertically. The map by itself it not a picture, it's a real world map. But when I click on certain location, a small picture will appear on the map.
I try to play with maxBounds and setMaxbounds. The normal maxBounds (to view the world map) is :
maxBounds: [[-85, -180.0],[85, 180.0]],
When i try to put the latitude to
[[-150, -180.0],[150, 180.0]]
, the vertical panning is still restricted. Can somebody help please? Thank you.
This sounds similar to a (quite obscure) issue in the Leaflet issue tracker a
while back: see https://github.com/Leaflet/Leaflet/issues/3081
However, that issue was dealing with infinite horizontal bounds, not vertical bounds in a CRS that already has some preset limits.
If you set the map's maxBounds to a value larger than 85 (the value for MAX_LATITUDE of L.Projection.Spherical) and run a debugger, the call stack goes through the map's _panInsideMapBounds(), then panInsideBounds(), then _limitCenter(), then _getBoundsOffset, then project(), then through the map CRS's latLngToPoint, then untimately L.Projection.Spherical's project(). L.Projection.Spherical.project() projects the bounds' limits into pixel coordinates, and clamps the projected point to be inside the projection's limits.
There are a lot of reasons behind this, one of them being to prevent users from putting markers outside the area covered with tiles:
(This is particularly important when a user confuses lat-lng with lng-lat and tries to use a value outside the [-90,90] range for latitude, and the projection code starts returning Infinity values everywhere)
How to get around this? Well, we can always specify the map's CRS, and we can create a CRS with a hacked projection which enforces a different limit. Please be aware that this changes how the pixelOrigin works internally (as explained in the Leaflet tutorial about extending layers), so stuff (particularly plugins) might break.
So something like:
var hackedSphericalMercator = L.Util.extend(L.Projection.SphericalMercator, {
MAX_LATITUDE: 89.999
});
var hackedEPSG3857 = L.Util.extend(L.CRS.EPSG3857, {
projection: hackedSphericalMercator
});
var map = new L.Map('mapcontainer', {
crs: hackedEPSG3857,
});
Of course, then you can set up your own maxBounds:
var map = new L.Map('mapcontainer', {
crs: hackedEPSG3857,
maxBounds: [[-Infinity, -10], [Infinity, 10]]
});
In this case, the bounds' limits would still be clamped to hackedSphericalMercator.MAX_LATITUDE, but you should have enough wiggle room for your application.
As a side note: A radically different approach to this problem would be to use a different map projection. We're used to a spherical cylindrical projection, but that's not the only way to flatten the earth.
In particular, a Transverse Mercator projection (or pretty much any other transverse cylindrical projection, for that matter) works pretty much in the same way, but wraps vertically instead of horizontally, and it's the projected longitudes, not latitudes, the ones which approach infinity asymptotically when approaching the [-180, 180] range. Let me borrow an image from its wikipedia article:
This implies a different set of challenges (namely finding some raster tiles appropriate for your application, including which prime meridian to use, and making proj4leaflet play nice), but it's definitely doable.
I want to zoom the google map after it exceeds it maximum zoom level. I google it and didn't find any solution. I am building a rooftop app any one can please help me on this.
The Google Maps API docs show that the google.maps.MapOptions object has a property called maxZoom that you can set when creating the map to set the maximum amount the map can be zoomed.
You can also set minZoom in the same way. So as long as your users don't need to be able to zoom very far out in order to move around effectively (e.g. if your app moves the map programmatically somehow) then you may even want to set the minZoom to a realistic value to ensure that people don't accidentally zoom too far out when trying to scroll down the page (I personally do that all the time with embedded google maps and it drives me crazy). But be careful with this: you don't want to ruin the user experience by removing your users' ability to navigate the map efficiently.
One thing you'll notice when reading the description of the maxZoom and minZoom parameters is that the docs don't specify the range of acceptable numbers. So how are you to figure out what numbers to pass in? And how do you change the max zoom after creating the map?
If the Map object is bound to a variable called map, then you can get the current zoom with map.getZoom() and you can set the current zoom with map.setZoom(zoom) where zoom is floating-point Number such that zoom >= 0. (Note: These methods get and set the "zoom" property of the Map object, but making changes directly to map.zoom will not cause the map to change, so be sure to use the getter and setter methods).
The most-zoomed-out level for the zoom property is 0, but the most-zoomed-in level will actually vary depending on where in the world the map is focused. (This makes sense, since street-level in some places could be a mile underground in others depending on elevation.) And at some zoom level, Google will no longer have (usable) satellite images.
Enter the MaxZoomService Class. This object's constructor can be found at google.maps.MaxZoomService. The instances of this class have only one useful method: getMaxZoomAtLatLng which takes a LatLng instance or a LatLngLiteral as its first parameter and a callback function to handle the result as its second.
A LatLng instance can be created with the google.maps.LatLng constructor, and a LatLngLiteral is just a normal object literal that has a lat property and a lng property. But since you'll probably be passing in a LatLng object from another method like map.getCenter(), you shouldn't have to worry about it.
Here's an example showing how to find out the maximum zoom available on the Satellite View at the center of the map's current view.
var mzs = new google.maps.MaxZoomService(); // Create a MaxZoomService instance
var mapCenterLatLng = map.getCenter(); // Get a LatLng instance
var handleMaxZoom = function(res) { // Callback Function accepting MaxZoomResult instance
if (res.status === "OK") {
// handle success
console.log("Max Zoom: " + res.zoom); // Print max possible zoom
map.setZoom(res.zoom); // Set the zoom to the max possible
} else {
// handle failure
console.log("Oops");
}
};
mzs.getMaxZoomAtLatLng(mapCenterLatLng, handleMaxZoom);
// => Max Zoom: [Number]
Edit:
If you really want to exceed the maximum zoom level (not just the default maximum zoom level as described above) your only real option is going to be using css transforms. Depending on how complicated the page layout is, this could end up being pretty difficult to get perfect.
I should note that this is typically not a good idea even if you can manage to get the layout to look good and the images to not be blurry. It carries all the same problems as using CSS to transform canvas elements (and some additional problems on top because it's not as simple as dealing with just a single DOM element). One notable problem is that applying CSS transforms will not affect the internal coordinates of the map (like those contained in click events) so you'll have to transform those every time you want to use them. Additionally, since the primary map div (the one returned by map.getDiv()) has to sit inside a container, this method will often cause it to overflow its container and you'll have to deal with that added complexity.
If you still want to do it, then here are some pieces of the puzzle that may help:
var mapCanvas = map.getDiv(); // The "main" div element that contains the map
var container = mapCanvas.parentElement; // You'll need some sort of container here
var scalingFactor = 1.5; // => Set a scaling factor (ex: 1.5 means scale it to 150% normal size)
/* Use CSS zoom property and/or vendor transforms to scale the map (though you could do this in straight CSS instead) */
mapCanvas.style.zoom = scalingFactor;
mapCanvas.style.MozTransform = 'scale(' + scalingFactor + ')';
mapCanvas.style.WebkitTransform = 'scale(' + scalingfactor + ')';
/* Then try to fit the scaled map back into its container, which will depend on how you're displaying it */
mapCanvas.style.width = "calc(100% / " + scalingFactor + ")"; // The CSS 'calc()' method might be useful here
I just want to clarify whether my way of understanding is correct. In my Google Maps app I would like to show to my users markers from particular continents.
Question: Am I right that a bound in Google Map API is an area made from NOT MORE AND NOT LESS than two points (markers)?
From math lessons, to draw 2D area I just need two points. Right? So to show to my users all markers from Europe should I just find two coordinates, one from Iceland (top, left) and second from let's say south-east Turkey? (For USA I would pick Seattle and Miami).
So, below JS code works perfectly. My question is - could you confirm that my way of understanding and the approach I've chosen is correct? Does it make any sense?
var bounds = new google.maps.LatLngBounds();
bounds.extend(new google.maps.LatLng('66.057878', '-22.579047')); // Iceland
bounds.extend(new google.maps.LatLng('37.961952', '43.878878')); // Turkey
this.map.fitBounds(bounds);
Yes, you are mostly correct. Except a 'bound' in Google maps case can include multiple points, for example if you had a point to the far right in the middle of the square in your image above and another at the bottom of the square in the middle you would still get an area the same as you have drawn above but with 3 points as in the edited map.
Obligatory link to the docs :)
https://developers.google.com/maps/documentation/javascript/reference?hl=en
You should not think about "top-left" and "bottom-right" but as south-west and north-east (so bottom-left and top-right if you like), as these are the coordinates used to create and/or retrieve the bounds object.
See the documentation and the getNorthEast and getSouthWest methods.
These two points are LatLng objects, and not markers. As an example, a marker is positioned on the map using a LatLng object.
I don't know your use case and your way of storing the data, but there might be a more precise way to display "Europe" markers. You could for example save the region along with each marker so that you can just query for "EU" markers and not others...
I am trying to learn how to use the Javascript library leaflet along with d3 to create various map visualisations.
I have been following this tutorial which creates a choropleth map of the United States with some interactivity. This provides some of what I need, but the main functionality I want is to have a list of lat/long coordinates classified according to which region they belong to.
This would mean, in the tutorial map for example, if I had a lat long value (55, -3) which fell within the state of Arizona's polygon, the program could classify this point as belonging to Arizona.
Is there a function in the leaflet (or d3) library which will allow me to enter a lat long coordinate as a parameter and return the name of the feature it belongs to? The tutorial above allows you to attach a function to every feature via the onEveryFeature property and can fire mouseover events when each feature is hovered over. Surely there is a way to extend this functionality to numerically entered data instead of mouse points?
Leaflet would need some tweaking if you wish to do this. It leaves the handling of mouseclicks to the browser and therefore does not need logic for determining if a point lies inside a polygon.
I am not very knowledgeable about d3 but it's not glaringly obvious to me how it'd do this out of the box. Looking at the polygon code, I do find a clipping algorithm and intersection of infinite lines.
If you add a third library, however, this should be rather simple.
The OpenLayers Geometry library can determine if a point lies inside a polygon.
EDIT: I got this to work, see also http://jsfiddle.net/VaY3E/4/
var parser = new OpenLayers.Format.GeoJSON();
var vectors = parser.read(statesData);
var lat = 36;
var lon = -96;
var point = new OpenLayers.Geometry.Point(lon, lat);
for( var i = 0; i< vectors.length; i++ ){
if(vectors[i].geometry.intersects(point)){
alert(vectors[i].attributes['name']);
}
}
Or you could use https://github.com/maxogden/geojson-js-utils , a bit more specific library. It looks like it knows how to read GeoJSON and it has a method gju.pointInPolygon. I've not tested it though.