Leaflet JS polygon on a same map - javascript

I use Leaflet JS, and I need same polygon on a one map
Now:
I need:
Thank's for your help!
If I set the noWrap:true on the tileLayer or the worldCopyJump:true I get:
Fiddle – jsfiddle.net/paRxe/5
var map = L.map('mapId',{
center: [35.67989, 139.76463],
zoom: 2,
// worldCopyJump: true,
maxZoom: 18,
minZoom: 1,
// reuseTiles: true,
// continuousWorld: trie
// reuseTiles: true,
// continuousWorld: true
worldCopyJump: true
}
);

Your 2 polygons are actually part of a multipolygon, forming a single feature.
You could use Turf.js for example, in order to 1) translate one of the part by 360 degrees, and 2) merge those 2 parts. Then record the new feature geometry to replace your current GeoJSON data.
For step 1), you should also be able to use directly Leaflet with latLng.wrap() method.

Related

Vertical alignment of TMS tiles in Leaflet using EPSG:25832 projection

I am using Leaflet with Proj4Leaflet to work with tiles in 25832. The application is fairly simple: I am trying to overlay tiles in EPSG:25832 onto a omniscale basemap. I have copied the individual resolutions and origin from the tilemap meta information. The problem I am facing is that the map is not aligned and once I zoom in the tiles are not placed in the correct order. I'd appreciate any kind of support here (by the way, this is a working example which is using openlayers).
I guess I am doing something wrong here:
// Set resolutions
var resolutions = [156367.7919628329,78183.89598141646,39091.94799070823,19545.973995354114,9772.986997677057,4886.4934988385285,2443.2467494192642,1221.6233747096321,610.8116873548161,305.40584367740803,152.70292183870401,76.35146091935201,38.175730459676004,19.087865229838002,9.543932614919001,4.7719663074595005,2.3859831537297502,1.1929915768648751];
// Define CRS
var rs25832 = new L.Proj.CRS(
'EPSG:25832',
proj4rs25832def,
{
origin: [ 273211.2532533697, 6111822.37943825 ],
resolutions: resolutions
}
);
...using the tiles information from https://mapproxy.bba.atenekom.eu/tms/1.0.0/privat_alle_50_mbit/germany .
Afterwards I add a tile layer
var url = 'https://mapproxy.bba.atenekom.eu/tms/1.0.0/privat_alle_50_mbit/germany/{z}/{x}/{y}.png';
var tileLayer = L.tileLayer(
url,
{
tms: true,
crs: rs25832,
continuousWorld: true,
maxZoom: resolutions.length
}
);
And add them to the map..
// Setup map
var map = L.map('map', {
crs: rs25832,
center: [ 50.8805, 7.3389 ],
zoom:5,
maxZoom: resolutions.length,
layers: [ baseWms, tileLayer ]
});
The bare minimum of code can be found here: https://jsfiddle.net/6gcam7w5/8/
This boils down to how the Y coordinate of TMS tiles is inverted (it becomes higher when going north, as opposed to default TileLayers, in which the Y coordinate becomes larger when going south).
Having a look on the Leaflet code that takes care of this specific feature will shed some light on the issue:
if (this._map && !this._map.options.crs.infinite) {
var invertedY = this._globalTileRange.max.y - coords.y;
if (this.options.tms) {
data['y'] = invertedY;
}
data['-y'] = invertedY;
}
There are two things critical to calculating the right Y coordinate for your tiles here:
The CRS must be finite (it must have bounds)
There must be a finite global tile range (which in Leaflet is ultimately defined by the CRS bounds and not the TileLayer bounds)
Long story short, your CRS should be defined with known bounds. For this particular case, taking information from the TMS capabilities document...
<BoundingBox minx="273211.2532533697" miny="5200000.0" maxx="961083.6232988155" maxy="6111822.37943825"/>
...and turned into a L.Bounds definition when defining the Leaflet CRS, like...
// Define CRS
var rs25832 = new L.Proj.CRS(
'EPSG:25832',
proj4rs25832def,
{
origin: [ 273211.2532533697, 6111822.37943825 ],
resolutions: resolutions,
bounds: [[273211.2532533697, 5200000],[961083.6232988155, 6111822.37943825]]
}
);
Stuff should just work (with no need to pass the CRS to the tilelayers, since they will all use the map's), as in this working example.

Leaflet: Getting Latitude/Longitude in createTile using CRS.Simple

I have a simple fictional map that I want to control using Leaflet. It is a flat 2D plane and its "latitude/longitude"/coordinate system spans from [0,0] to [999,999].
I have customized the map as follows:
window.map = L.map('leaflet-map', {
crs: L.CRS.Simple,
center: [500, 500],
zoom: 13,
maxBounds: [
[0, 0],
[999, 999],
],
layers: [new MyLayer()],
});
To draw this map, I've created a new layer, MyLayer, which extends gridLayer:
export var MyLayer = GridLayer.extend({
createTile: function(coords, done) {
var error;
var xmlhttprequest = new XMLHttpRequest();
xmlhttprequest.addEventListener('readystatechange', function() {
done(error, dothething());
});
xmlhttprequest.open('GET', /* url */);
xmlhttprequest.send();
},
});
The problem I have is the URL accepts the [0,0] to [999,999] coordinate system as parameters but I can't find how to actually get those. I understand there may be some decimal element but I can floor that as appropriate.
When centered on [500, 500, 13] the coords object contains { x: 15516, y: -21558, z: 13 }. When passed to L.CRS.Simple.pointToLatLng(coords, coords.z) I get { lat: 2.631591796875, lng: 1.89404296875 }.
I've downloaded the source code in an attempt to understand how this transformation happens from Map._move(center, zoom, data) but all that appears to do is call this.options.crs.latLngToPoint(), which is exactly what I reverse in L.CRS.Simple.pointToLatLng. I'm frankly at a loss.
First of all, I encourage you to read the Leaflet tutorial on L.CRS.Simple once again. Let me quote a relevant bit from there :
In a CRS.Simple, one horizontal map unit is mapped to one horizontal pixel, and idem with vertical. [...] we can set minZoom to values lower than zero:
So you have no reason to go down to zoom level 13 on your L.CRS.Simple map by default, really. For a [0,0]-[999,999] map, use zoom level zero for an overview, or use map.fitBounds([[0,0],[999,999]]).
The values that the createTile() method receives are tile coordinates, not CRS coordinates. A level-0 tile is split into four level-1 tiles, sixteen level-2 tiles, 64 level-3 tiles, and so on, up to 2^13 tiles at level 13. This is easier to visualize by playing with a L.GridLayer that displays the tile coordinates, like:
var grid = L.gridLayer({
attribution: 'Grid Layer',
// tileSize: L.point(100, 100),
});
grid.createTile = function (coords) {
var tile = L.DomUtil.create('div', 'tile-coords');
tile.innerHTML = [coords.x, coords.y, coords.z].join(', ');
return tile;
};
map.addLayer(grid);
Second: you want to use the internal _tileCoordsToBounds method, defined at L.GridLayer. Give it a set of tile coordinates, and you'll get back a L.LatLngBounds with the area covered by such a tile.
The following example (try it live here) should put you on track. Remember to read the documentation for L.LatLngBounds as well.
var grid = L.gridLayer({
attribution: 'Grid Layer',
// tileSize: L.point(100, 100),
});
grid.createTile = function (coords) {
var tile = L.DomUtil.create('div', 'tile-coords');
var tileBounds = this._tileCoordsToBounds(coords);
tile.innerHTML = [coords.x, coords.y, coords.z].join(', ') +
"<br>" + tileBounds.toBBoxString();
return tile;
};
map.addLayer(grid);

Is there a way make a reusable shape object?

I'm using LeafletJS to make a custom lab layout map and I plan to put in some rectangle layers to show whether or not areas are in use. Currently I have a working test case using coordinates to define each shape, but is there a way I can create a standard sized shape object which can be called and fed a single coordinate to center itself on?
Here is the current code from my Angular controller if it helps.
function showMap() {
var map = L.map('mapid', {
crs: L.CRS.Simple,
maxZoom: 4,
attributionControl: false
}).setView([0, 0], 1),
southWest = map.unproject([0, 4096], map.getMaxZoom()),
northEast = map.unproject([4096, 0], map.getMaxZoom()),
bounds = L.latLngBounds(southWest, northEast);
L.tileLayer('images/4231/{z}/{x}/{y}.png', {
minZoom: 1,
maxZoom: 4,
center: [0, 0],
noWrap: true,
bounds: bounds
}).addTo(map);
var testBench = [{
number: "1A1",
coord1: "-48.6",
coord2: "6",
coord3: "-81.4",
coord4: "71",
inUse: true
}, {
number: "1A2",
coord1: "-48.5",
coord2: "71",
coord3: "-81",
coord4: "137",
inUse: false
}, {
number: "1A3",
coord1: "-48.5",
coord2: "137",
coord3: "-81",
coord4: "202",
inUse: true
}];
angular.forEach(testBench, function(item, index) {
var location = [
[item.coord1, item.coord2],
[item.coord3, item.coord4]
],
color;
switch (item.inUse) {
case true:
color = "red"
break;
case false:
color = "green"
break;
}
L.rectangle(location, {
color: color,
weight: 1
}).bindPopup("Bench Number is: " + item.number).addTo(map);
})
map.setMaxBounds(bounds);
}
Eventually the bench info will be pulled from a DB rather than a variable and there will be hundreds of benches, so I'm looking to streamline the positioning layout as much as possible.
is there a way I can create a standard sized shape object which can be called and fed a single coordinate to center itself on?
In Leaflet, no.
Leaflet vector features, or L.Paths (L.Polylines and L.Polygons) are defined by their coordinates, not by their centroid and a series of offsets to that centroid.
You might want to implement a simple Factory design pattern to create regularly-shaped features giving only a center point, though.

How do switch Leaflet coordinates to where 0,0 is southwest?

I am working on a leaflet project where I need the coordinates 0 to 4096 and which the leaflet coordinates that I need are the following:
0,0 bottom left (southWest)
0,4096' top left (northWest)
4096',4096' top right (northEast)
4096',0 bottom right (southEast)
I have tried many things to get the coordinates to map as needed but no luck. Here is my jsfiddle with my example, which has a console.log when you click on the map which shows the coordinates. If you click in the top left you will see its 0,0 and if you click in the bottom right you will see 4096,4096 which is backwards from whats needed. Here is my code that I have
url = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png';
weight = 4096;
height = 4096;
mapMinZoom = 1;
mapMaxZoom = 7;
map = L.map('map', {
maxZoom: mapMaxZoom,
minZoom: mapMinZoom,
noWrap: true,
detectRetina: true
});
unproject = function(coord) {
return map.unproject(coord, 4);
};
southWest = unproject([0, 0]);
northEast = unproject([height, weight]);
map.setMaxBounds(new L.LatLngBounds(southWest, northEast));
tileLayer = L.tileLayer(url, {
minZoom: mapMinZoom,
maxZoom: mapMaxZoom,
noWrap: true,
tileSize: 256,
detectRetina: false
}).addTo(map);
map.setView(unproject([0, 0]), 2);
map.on('click', function(e) {
var coords, latLong;
latLong = new L.LatLng(e.latlng.lat, e.latlng.lng);
coords = map.project(latLong, 4);
console.log("x="+coords.x+", y="+coords.y);
});
If I understand correctly, you would like a map coordinates like this:
[0, 4096] .. [4096, 4096]
.........................
.........................
[0, 0] ........ [4096, 0]
It is good as vertical coordinate increases while going up (like latitude on Leaflet) and horizontal coordinate increases while going right (like longitude on Leaflet). So you do not seem to need to invert the vertical axis.
As for the order of the coordinates, unfortunately Leaflet uses latitude first, longitude second, and it is almost impossible to switch them natively. However, you can easily build a wrapper method that just swaps the coordinates. (similar to Leaflet LatLngBounds with simpler CRS & projection, but no need to use negative vertical coordinate -y since in your case you want it in the same direction as latitude)
Then another question would be how you want the coordinates in-between those 4 corners. The default CRS (Web Mercator) is not linear along the vertical axis. If you need your coordinates to be linear, you should use L.CRS.Simple. See Leaflet tutorial for non-Earth-based maps.

Highlight polygon and tint rest of map using Google Maps [duplicate]

This question already has an answer here:
Google Maps API v3 Highlight Country Border without using Polygons
(1 answer)
Closed 8 months ago.
I'd like to display a highlighted polygon using Google Maps. The idea is that the polygon in question would be displayed normally and the rest of the map should be darkened a little bit.
So, is this even possible do this with Google Map API's? If yes, with what version (v2, v3)? Would it be easier to do it with other map toolkits, like openlayers?
PS: One idea I had, was to build an inverse polygon (in this example, the whole world minus the shape of austria) and then display a black colored overlay with transparency using this inverted polygon. But that seems to be quite complicated to me.
Google Maps API v3 lets you draw polygons with holes. Here's Google's Pentagon example. It is much easier than trying to invert a polygon. Basically, create coordinates for a giant polygon that is bigger than you would ever need. That will always be the first polygon in your polygon array. The area you are highlighting will always be the second polygon.
Here's some code to change Google's Bermuda Triangle demo to use a polygon with a hole:
var everythingElse = [
new google.maps.LatLng(0, -90),
new google.maps.LatLng(0, 90),
new google.maps.LatLng(90, -90),
new google.maps.LatLng(90, 90),
];
var triangleCoords = [
new google.maps.LatLng(25.774252, -80.190262),
new google.maps.LatLng(18.466465, -66.118292),
new google.maps.LatLng(32.321384, -64.75737),
new google.maps.LatLng(25.774252, -80.190262)
];
bermudaTriangle = new google.maps.Polygon({
paths: [everythingElse, triangleCoords],
strokeColor: "#000000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#000000",
fillOpacity: 0.5
});
bermudaTriangle.setMap(map);
USING GEOJSON
<div id="googleMap" style="width:500px;height:380px;"></div>
// define map properties
var mapProp = {
center: new google.maps.LatLng(23.075984, 78.877656),
zoom: 5,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
//create google map
var map = new google.maps.Map(document.getElementById("googleMap"), mapProp);
// define geojson
var geojson = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[0, 90],
[180, 90],
[180, -90],
[0, -90],
[-180, -90],
[-180, 0],
[-180, 90],
[0, 90]
],
[
[79.56298828125, 25.18505888358067],
[76.53076171875, 21.37124437061832],
[83.38623046875, 21.24842223562701],
[79.56298828125, 25.18505888358067]
]
]
},
"properties": {}
}]
};
//add geojson to map
map.data.addGeoJson(geojson);
incase of external geojson file use
map.data.loadGeoJson('url-to-geojson-file');
note: google used .json as extension for geojson file and not .geojson
https://developers.google.com/maps/documentation/javascript/datalayer
create your geojson here
https://google-developers.appspot.com/maps/documentation/utils/geojson/
working example
https://jsfiddle.net/841emtey/5/
With regard to:
the rest of the map should be darkened a little bit.
This can be done with with Maps API v3 using Styled Maps.
There's even a Styled Maps Wizard where you can play with the settings, and then press Show JSON to get the array to pass as the first argument to new google.maps.StyledMapType.
To get the effect you want just reduce the Lightness for everything, in the wizard you'll want to see this in the Map Style box on the right:
Feature type: all
Element type: all
Lightness: -70
Which will export to:
[
{
"stylers": [
{ "lightness": -70 }
]
}
]
And look like this.

Categories

Resources