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.
Related
This question already has answers here:
D3.js Drawing geojson incorrectly
(2 answers)
Geojson map with D3 only rendering a single path in a feature collection
(3 answers)
Closed 2 years ago.
Currently I'm working a D3 project to render map with GeoJSON. I found this great example online https://bl.ocks.org/john-guerra/43c7656821069d00dcbc
and trying to implement with my GeoJSON map, while when I edited parameters according to new JSON file, the html only rendered partial map and showing in a rectangle. I tried to inspect the code but no clue where is wrong. Please share some hints where I can get the map display correctly. Full codes can be found on git https://github.com/gracemagy/singapore-map-D3
Thank you in advance!
Here is the code where I edited from original script:
updated targeted JSON file name
d3.json('electoral-boundary-dataset.geo.json', function(error, mapData) {
var features = mapData.features;
d.properties.NOMBRE_DPT to d.properties.Name
function nameFn(d){
return d && d.properties ? d.properties.Name : null;
}
scale(1500) to scale(50) to zoom out the map
var projection = d3.geo.mercator()
.scale(50)
Seems to me like there might be a problem with your geoJSON. It's like it has a frame around the map, and maybe that's the square you are looking at. If you make the fill of your example "none" and draw the stroke, you can see that your map is looking tiny inside this big square frame.
As #andrew-reid commented, this seems to be a winding problem. Check his comment on the original answer
Using the geoJSON from this bl.ock I was able to craft this example:
https://observablehq.com/d/21415ae86b8e5610
That doesn't have everything you want, but at least shows the map. Notice how it uses a projection that fits everything into the width and height
var projection = d3.geoMercator().fitSize([width, height], geoJSON);
Also, try not to use d3.v3, that's way too old.
Here is a working version:
https://observablehq.com/#john-guerra/geojson-singapur
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.
There is a know problem of Leaflet that when you use a custom tile provider, not with real earth images, set crs: L.CRS.Simple, Leaflet queries for images where Y coordinate is inverted in comparison to the math axis. So the first top-right image's location is 1x-1 instead of 1x1.
In the internet topics about inverting Y axis are rather old, so my question is: nowadays is there a normal short and built-in way to invert queried Y axis?
The only old solutions I've found were rewriting Leaflet internal objects, like extending L.CRS.Simple.
As noted in the Leaflet tutorial for WMS/TMS, the canonical way of inverting the Y coordinate for tile coordinates is using {-y} instead of {y} in the tile URL template. e.g.:
var layer = L.tileLayer('http://base_url/tms/1.0.0/tileset/{z}/{x}/{-y}.png');
Note, however, that (as of Leaflet 1.3.1) that only works for maps with a non-infinite coordinate system.
In your case, you might want to get around this by creating your own subclass of L.TileLayer. There is a complete guide on the Leaflet tutorial about extending layers, but the TL;DR version for a tilelayer that shifts its tile coordinates is:
L.TileLayer.CustomCoords = L.TileLayer.extend({
getTileUrl: function(tilecoords) {
tilecoords.x = tilecoords.x + 4;
tilecoords.y = tilecoords.y - 8;
tilecoords.z = tilecoords.z + 1;
return L.TileLayer.prototype.getTileUrl.call(this, tilecoords);
}
});
var layer = new L.TileLayer.CustomCoords(....);
And the specific case for just inverting the Y coordinate is:
L.TileLayer.InvertedY = L.TileLayer.extend({
getTileUrl: function(tilecoords) {
tilecoords.y = -tilecoords.y;
return L.TileLayer.prototype.getTileUrl.call(this, tilecoords);
}
});
var layer = new L.TileLayer.InvertedY(....);
OpenLayers supports tissot's ellipses natively by adding a sphere to the circular() method.
Unfortunately the Leaflet L.circle() does not support such feature.
How do I draw tissot's ellipses with Leaflet?
EDIT 3:
New proposition using leaflet-geodesy which seems a perfect fit for your need. It is exempt from Turf's bug (see Edit 2 below).
The API is quite simple:
LGeo.circle([51.441767, 5.470247], 500000).addTo(map);
(center position in [latitude, longitude] degrees, radius in meters)
Demo: http://jsfiddle.net/ve2huzxw/61/
Quick comparison with nathansnider's solution: http://fiddle.jshell.net/58ud0ttk/2/
(shows that both codes produce the same resulting area, the only difference being in the number of segments used for approximating the area)
EDIT: a nice page that compares Leaflet-geodesy with the standard L.Circle: https://www.mapbox.com/mapbox.js/example/v1.0.0/leaflet-geodesy/
EDIT 2:
Unfortunately Turf uses JSTS Topology Suite to build the buffer. It looks like this operation in JSTS does not fit a non-plane geometry like the Earth surface.
The bug is reported here and as of today the main Turf library does not have a full workaround.
So the below answer (edit 1) produces WRONG results.
See nathansnider's answer for a workaround for building a buffer around a point.
EDIT 1:
You can easily build the described polygon by using Turf. It offers the turf.buffer method which creates a polygon with a specified distance around a given feature (could be a simple point).
So you can simply do for example:
var pt = {
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [5.470247, 51.441767]
}
};
var buffered = turf.buffer(pt, 500, 'kilometers');
L.geoJson(pt).addTo(map);
L.geoJson(buffered).addTo(map);
Demo: http://jsfiddle.net/ve2huzxw/41/
Original answer:
Unfortunately it seems that there is currently no Leaflet plugin to do so.
It is also unclear what the Tissot indicatrix should represent:
A true ellipse that represents the deformation of an infinitely small circle (i.e. distortion at a single point), or
A circular-like shape that represents the deformation of a finite-size circle when on the Earth surface, like the OpenLayers demo you link to?
In that demo, the shape in EPSG:4326 is not an ellipse, the length in the vertical axis decreases at higher latitude compared to the other half of the shape.
If you are looking for that 2nd option, then you would have to manually build a polygon that represents the intersection of a sphere and of the Earth surface. If my understanding is correct, this is what the OL demo does. If that is an option for you, maybe you can generate your polygons there and import them as GeoJSON features into Leaflet? :-)
Because turf.buffer appears to have a bug at the moment, here is a different approach with turf.js, this time using turf.destination rotated over a range of bearings to create a true circle:
//creates a true circle from a center point using turf.destination
//arguments are the same as in turf.destination, except using number of steps
//around the circle instead of fixed bearing. Returns a GeoJSON Polygon as output.
function tissotish(center, radius, steps, units) {
var step_angle = 360/steps;
var bearing = -180;
var tissot = { "type": "Polygon",
"coordinates": [[]]
};
for (var i = 0; i < steps + 1; ++i) {
var target = turf.destination(center, radius, bearing, units);
var coords = target.geometry.coordinates;
tissot.coordinates[0].push(coords);
bearing = bearing + step_angle;
}
return tissot;
}
This doesn't produce a true Tissot's indicatrix (which is supposed to measure distortion at a single point), but it does produce a true circle, which from your other comments seems to be what you're looking for anyway. Here it is at work in an example fiddle:
http://fiddle.jshell.net/nathansnider/58ud0ttk/
For comparison, I added the same measurement lines here as I applied to ghybs's answer above. (When you click on the lines, they do say that the horizontal distance is greater than 500km at higher latitudes, but that is because, the way I created them, they end up extending slightly outside the circle, and I was too lazy to crop them.)
This question already has answers here:
Google Maps API v3 Polygon closing
(2 answers)
Closed 8 years ago.
When working with polygons on google maps, there can be cases where a polygon will cross the map overlap. For example, when going to the right edge of the map, and placing points to create a box, with the left points on the right edge of the map, and the right points on the left edge of the repetition of the map (as the map can pan infinitely horizontally).
That said, I would see points close to the +180 and -180 extremes such as:
[50, 178] top left
[50, -178] top right
[-50, -178] bottom right
[-50, 178] bottom left
This is the data provided when pulled out from looping the polygon.getPath() points.
google.maps.event.addListener(drawingManager, 'polygoncomplete', function(polygon) {
polySelected = true;
coords = new Array();
polygon.getPath().forEach(function (l, index) {
coords[index]="("+l.lat()+","+l.lng()+")"
});
}
With this data, it is impossible to tell if the points actually cross the 180 meridian line. If I took out the 'upper left', 'upper right', you would have no way of discerning if the users second point was to the right (crossing 180) or left (making a very large box).
Is there any way around this? Such as not having 'wrapped' coordinates [50, 182] like that? What is the standard way of dealing with crossing the boundaries and having data representing that?
Thanks!
Edit - This is not a case of closing the polygon and having coordinates snap to a very large area. This case is for a normal polygon manual closure, creating a small square polygon, but one that crosses the map horizontal repeat and having the resultant coordinates be indistinguishable from a polygon that crosses the repeat 180 meridian line, or one that is very wide (but no different vertically) and covering the opposite area. I wish I could demonstrate with pictures to help explain but I do not have this functionality yet.
Answer
I do not see a way to answer my own question, however I have figured it out and want to share. I realized that you cannot draw an edge of a polygon that extends more than 180 degrees longitudinally. Knowing that, if you have two points that have a different of greater than 180, it crosses the +/-180 meridian.
Thanks to all who were offering support!
You can do this:
var longitude = l.lng();
while (longitude > 180)
longitude -= 360; // e.g. 182 and 542 become -178, which is the equivalent longitude
while (longitude < -180)
longitude += 360;
coords[index]="("+l.lat()+","+longitude+")";
As long as your latitude is between 90 and -90. If it isn't you need to fix that too... let me know if you need help with that.