I'm using Leafletjs with D3 to show a map.
I only want to show the UK in the map.
Is it possible with Leaflet and D3 to only show the UK.
It is certainly possible.
Now the solution depends whether you want to use D3 to draw the UK, or you want to get it from a Tile Server.
In the latter case, there is a more recent solution than the one proposed in the link in xmojmr's comment.
See Prevent tiles outside of polygon from loading
The idea is to use the TileLayer.BoundaryCanvas plugin, which you can specify a GeoJSON boundary option to hide everything that is not within that GeoJSON geometry.
BoundaryCanvas is a plugin for Leaflet mapping library to draw tiled raster layers with arbitrary boundary. HTML5 Canvas is used for rendering.
Live demo with a rough UK shape:
var map = L.map("map");
$.getJSON('https://cdn.rawgit.com/johan/world.geo.json/34c96bba/countries/GBR.geo.json').then(function(geoJSON) {
var osm = new L.TileLayer.BoundaryCanvas("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
boundary: geoJSON,
attribution: '© OpenStreetMap contributors, UK shape johan/word.geo.json'
});
map.addLayer(osm);
var ukLayer = L.geoJSON(geoJSON);
map.fitBounds(ukLayer.getBounds());
});
#map {
height: 500px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.2.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.2.0/dist/leaflet-src.js"></script>
<script src="https://cdn.rawgit.com/aparshin/leaflet-boundary-canvas/f00b4d35/src/BoundaryCanvas.js"></script>
<div id="map"></div>
Related
I aim to overlay a tile layer on top of another. The first tile layer is created using L.tileLayer() using the url: baseUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' and the second tile layer is created using a wms service (L.tileLayer.wms) hosted on geoserver which is serving some .tif images as tiles. I want to create something like: https://alpha.anthropo.co/znz-demo
As can be seen in the demo, once the user zooms out one can see the base map and after a specific zoom level, the .tif images can also be seen on top of the base map.
In my implementation, I can only see either one tile layer or another. And after a certain zoom level, I can see the geoserver tile layer but cannot see the osm layer at all and I suppose that it might be because the osm tilelayer might not be supporting zoom levels that high and hence I am only seeing the geoserver tile layer.
I have tried creating/setting pane levels, making the served tiles transparent using geoserver, layer groups, setting transparent param to true and format param to 'image/png' at the same time when creating the tilelayer, and using the layer.bringToFront() method but none of these seem to help in this case or maybe they do but because I am a newbie to leaflet/web mapping world I am not using them the right way.
geoserver footprint behaviour and merge behaviour configuration screenshot
It would be great if you can help me with this :) Thanks...!
My code is as follows:
<!DOCTYPE html>
<html>
<head>
<!-- Title -->
<title> Sample: Loading tiles from Geoserver </title>
<!-- Including Leaflet CSS file in the head section of the document. -->
<link rel="stylesheet" href="./leaflet_js/leaflet.css" />
<link rel="stylesheet" href="./style.css" />
<!-- Including Leaflet JavaScript file after Leaflet's CSS file. -->
<!-- Make sure you put this AFTER Leaflet's CSS -->
<script src="./leaflet_js/leaflet.js"></script>
</head>
<body>
<div id="map"></div>
<script>
// Latitude and Longitude of the point that is displayed when the map first loads.
var startPoint = [latitude=-5.733, longitude=39.298]
// Creating map options
var mapOptions = {
center: startPoint,
zoom: 15,
crs: L.CRS.EPSG3857,
};
// Creating a map object
var map = L.map('map', mapOptions);
var tanzania_layer = L.tileLayer.wms(
baseUrl='http://localhost:8080/geoserver/OpenAI_Tanzania/wms?', {
layers: 'train',
maxZoom: 25,
crs: L.CRS.EPSG3857,
noWrap: true,
tranparent: true,
format: 'image/png'
});
var osm_layer = L.tileLayer(
baseUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 25,
attribution: '© OpenStreetMap',
crs: L.CRS.EPSG3857,
noWrap: true,
tranparent: true,
format: 'image/png'
});
baseLayer = L.layerGroup([tanzania_layer, osm_layer]);
map.addLayer(baseLayer);
</script>
</body>
</html>
My output
I have taken care of the crs and if I only show the geoserver tile layer then I can see the data here so the layers align as well.
I would like to see the geoserver tile layer data on top of the current osm layer that is shown in the output screenshot.
Thanks again :)
I have a Leaflet map with a geoJSON containing contour lines. The elevation corresponding to each line is located in feature.properties.Elevation of the geoJSON. I want to achieve the following:
What I've tried to achieve this in leaflet is to calculate the center of each polyline and then add a marker to that position containing the data from feature.properties.Elevation.
L.geoJson(contourJSON, {
onEachFeature: function(feature, layer) {
var label = L.marker(layer.getBounds().getCenter(), {
icon: L.divIcon({
className: 'label',
html: feature.properties.Elevation,
iconSize: [100, 40]
})
}).addTo(map);
}
});
Which kind of works, but does not have a nice styling and it's not really clear which value belongs to a specific line:
What would be a better method of adding the elevation labels in such a way that it is readable and maybe dynamic to the current zoom level? I'm using Leaflet 1.0.3 so maybe Tooltip could offer a solution? Thanks!
You might be interested in those Leaflet plugins, possibly combined: (not sure how easy it would be to combine them)
Leaflet.LabelTextCollision (demo)
…display[s] labels on vector data while avoiding label collisions.
Leaflet.TextPath (demo)
Shows a text along a Polyline.
You might also want to check out the rest of Leaflet plugins.
BTW, I am not sure placing your label / marker at the polyline "center" is appropriate. You might just pick one of its vertices, or for example the farthest to the right / East to have an effect similar to the example you provide.
I'm using AngularJS, Leaflet, and Angular-leaflet-directive in the following example which I got from the Angular-leaflet-directive project. This example works when using public tile servers, including the public ArcGIS server the example is currently pointing to.
According to several online sources of documentation, including these two from open street maps and mapbox, the number of tiles available at a particular zoom level should be 2^z X 2^z, where z is the zoom level. So, at zoom level 0 we should get 1 tile, at 1 we should get 4 tiles, and so on.
The issue I have only occurs when pointing to a privately hosted (corporate intranet) ESRI ArcGIS tile server. When I try to view the page below as is, but with the private tile server's url instead of the http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}, I only see partial maps at zoom level 0 and 1.
After poking around a little bit I realized that the tile server wants to send two tile instead of 1 at zoom level 0, 6 tiles instead of 4 at zoom level 1. And the problem continues at deeper zoom levels. When looking at the urls for the individual tiles (for example ../0/0/0 and ../0/0/1 at zoom level 0), I see that we do get the composite map we expect if we put together remaining tiles which are available at that zoom level. Meaning if leaflet wanted to build the entire map the information is available.
I am very new to Leaflet and Maps in general, but I feel that by telling leaflet to expect more tiles at a given zoom level, it should be able to get the complete picture, but after searching leaflet documentation, I was unable to find such a configuration.
My questions: is there such a setting? If so what is it? Maybe the question is missing something or I don't know enough to ask the right question. Am I looking at the right documentation? Any guidance would be appreciated.
<html>
<head>
<title>A Leaflet map!</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css"/>
<script src="http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
<style>
#map{ width: 900px; height: 500px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// initialize the map
var map = L.map('map').setView([42.35, -71.08], 13);
// load a tile layer
L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
{
maxZoom: 17,
minZoom: 9
}).addTo(map);
</script>
</body>
</html>
Maybe the question is missing something or I don't know enough to ask the right question.
I think you're missing the concept of map projections. If a map is using EPSG:3857, then chances are that the zoom 0 is covered by 1 tile. If it's in EPSG:4326, then it's 2 tiles.
Compare these (static) images from leaflet maps of NASA's Blue Marble:
Both are correct, but they use different projections (EPSG:3857 on top and EPSG:4326 on bottom).
I strongly suggest you research what projection your tiles are using. You might need a bit of extra research on map projections and even Proj4Leaflet to get it working, but at least you know ehre to look.
Here is my solution based on comments from #IvanSanchez. Note that I'm pointing to a public server that serves up EPSG:4326 and adding crs:L.CRS.EPSG4326 to defaults. One important point to note is that this is not fully supported by the current stable version of leaflet (0.7.7). In order to get it to work, I upgraded to 1.0.0 Beta2. For more information on this issue, including alternative to using the beta version, see https://github.com/Leaflet/Leaflet/issues/1207.
<!DOCTYPE html>
<html ng-app="demoapp">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="lib/angular-1.4.2/angular.min.js"></script>
<script src="lib/leaflet/dist/leaflet.js"></script>
<script src="lib/angular-leaflet-directive-master/dist/angular-leaflet-directive.min.js"></script>
<link rel="stylesheet" href="lib/leaflet/leaflet.css" />
<script>
var app = angular.module("demoapp", ["leaflet-directive"]);
app.controller('BasicCustomParametersController', [ '$scope', function($scope) {
angular.extend($scope, {
london: {
lat: 51.505,
lng: -0.09,
zoom: 1,
noWrap: false,
minZoom: 1,
maxZoom: 10
},
defaults: {
//EPSG:3857
// tileLayer: "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}",
//EPSG:4326
tileLayer: "http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer/tile/{z}/{y}/{x}",
zoomControlPosition: 'topright',
tileLayerOptions: {
opacity: 0.9,
detectRetina: true,
reuseTiles: true,
},
scrollWheelZoom: false,
//use EPSG4326. Not fully supported in leaflet 0.7.7. Works with 1.0.0 Beta2
crs:L.CRS.EPSG4326
}
});
}]);
</script>
</head>
<body ng-controller="BasicCustomParametersController">
<leaflet lf-center="london" defaults="defaults" width="100%" height="480px"></leaflet>
<h1>Using custom default parameters</h1>
</body>
</html>
I am trying to create a presentation running on top of Reveal.js, which would include a Leaflet.js map within one of the slides. I have included all necessary Javascript & CSS files into my Reveal.js presentation and I can make the map appear on the slide.
However, the problem is: map tiles are not displayed correctly. Instead of the actual map tiles, all I am seeing is gray background and some horizontal black lines. I can zoom in/out and pan the map, and the black lines are moving accordingly.
There are no error message in the Javascript console, and the browser seems to be downloading map tiles from server exactly as it should. I believe the problem has something to do with the CSS code of Leaflet map tiles - .leaflet-tile within leaflet.css - being somehow incompatible with Reveal.js.
The question is: Does anyone know how to get around this issue? Or is it a deadend with no possible solution?
I have the following CSS for the <div id="map">:
#map {
height:400px;
width:100%;
}
EDIT: One obvious workaround for this is to use <iframe> tag to embed the map into the presentation. Seems to work just fine, and maybe it is better to keep the frameworks separated. However, the downside is that if there are several maps in the presentation, each within its own <iframe>, a copy of Leaflet.js is loaded to memory for each and every iframe.
EDIT #2: A better solution, it seems, is to use Polymaps instead of Leaflet.js. It seems that several Polymaps maps can be embedded into a reveal.js presentaion. No issues.
I found it easily to do it with a web component, this way, the shadow dom will protect my leaflet map from the evil hands of reveals css
here is a repo with an example
<link rel="import" href="./leaflet-map.html">
...
<div class="reveal">
<div class="slides">
<section data-state="map">
<leaflet-map></leaflet-map>
</section>
</div>
</div>
Here is the web component
<template id="leaflet-map-template">
<link rel="stylesheet" href="./bower_components/leaflet/dist/leaflet.css">
<div id="mapid" style="height: 500px"></div>
<!-- LEAFLET JS -->
</template>
<script src="./bower_components/leaflet/dist/leaflet.js"></script>
<script>
class LeafletMap extends HTMLElement {
constructor () {
super();
let tmpl = document.currentScript.ownerDocument.querySelector('template')
let shadowRoot = this.attachShadow({mode: 'open'})
shadowRoot.appendChild(tmpl.content.cloneNode(true))
let mapDiv = this.shadowRoot.getElementById('mapid')
this.map = L.map(mapDiv).setView([19.39682052576622, -99.13478851318361], 13)
// this.setAttribute('map', map)
// Tiles de open street maps
//L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map)
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(this.map)
let myIcon = L.icon({
iconUrl: './lentes.png',
iconSize: [40, 40], // size of the icon
iconAnchor: [20, 20], // point of the icon which will correspond to marker's location
tooltipAnchor: [20,0]
})
L.marker(
[19.418657758792698, -99.14065182209016],
{icon: myIcon}
).bindTooltip('Ranchito').addTo(this.map)
}
resize() {
this.map.invalidateSize()
}
}
window.customElements.define('leaflet-map', LeafletMap)
</script>
It might be happening because the #map element is hidden (due to the hidden slide) when it is initialized, so it cannot read the dimensions..
Try using map.invalidateSize(false); once your slide becomes visible..
Reveal.addEventListener( 'slidechanged', function( event ) {
// event.previousSlide, event.currentSlide, event.indexh, event.indexv
if (event.indexh == 5){ // assuming your 5th slide is the one with the map
map.invalidateSize(false); // assuming that map holds the the reference to your leaflet instance
}
} );
I'm trying to create a small example using Leaflet and use it on my web app, however I'm having some dificulties.
I already made a standalone Leaflet example from the quick start guide of their page, which has a static map div and overlays a few objects (marker, circle and polygon).
However in my app, the map div container is positioned and sized dynamically with jquery, and it's a pretty complex app so I can't post here the whole code.
The problem is that after the correct processing of the div the map is drawn correctly in the div (wherever it is), however in the overlays layer I can only see the marker. The circle and polygonare not visible.
var map = L.map(this.$el.attr('id')).setView([51.505, -0.09], 13);
L.tileLayer('http://{s}.tile.cloudmade.com/<APIKEY>/997/256/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © CloudMade',
maxZoom: 18
}).addTo(map);
var circle = L.circle([51.508, -0.11], 500, {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
}).addTo(map);
var polygon = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
]).addTo(map);
var marker = L.marker([51.5, -0.09]).addTo(map);
marker.bindPopup("<b>Hello world!</b><br>I am a popup.");
circle.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
I believe there's nothing wrong in the way I create the map and objects and I even call map.invalidateSize() after the whole resizing of the div. And the curious part is that the popup is shown in the correct position, but no circle behind it.
Could the problem be in the way I resize and position the div containers. Is there any specific CSS property I should be using in the div container?
Thanks for all the help.
After sleeping on this problem I thought about making a JSFiddle example to reproduce the issue.
However no matter how hard I tried I couldn't replicate the issue in a simpler standalone example, so the problem had to be in some conflict within my app.
Every symptom pointed to a CSS problem, however my CSS was well scoped.
So my last theory was that it had to be a conflict in some library I was using.
It turned out to be correct. The problem was a conflict in the CSS stylesheet of the library NVD3