Overlay a tile layer on top of another tile layer using leaflet - javascript

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 :)

Related

openlayers map center issue

I am very new to working with openlayers. The tutorial below teaches how to show a map and build local, but the problem is I can not change the center of the map. Even If I change the location, it shows aways the "zero" point of the map.
https://openlayers.org/en/latest/doc/tutorials/bundle.html
below are the .js example and .html, the problem is the center: I can't change the map center
import 'ol/ol.css';
import {Map, View} from 'ol';
import TileLayer from 'ol/layer/Tile';
import OSM from 'ol/source/OSM';
const map = new Map({
target: 'map',
layers: [
new TileLayer({
source: new OSM()
})
],
view: new View({
center: [0, 0],
zoom: 0
})
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Using Parcel with OpenLayers</title>
<style>
#map {
width: 400px;
height: 250px;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="./index.js"></script>
</body>
</html>
Every GIS map has a coordinate system. GIS web applications are very similar to desktop GIS maps, so they have a coordinate system too. in OpenLayers known as projection.
In OpenLayers, every web app made up of a Map() class that needs a view() to show the map. By default, every view() use Web Mercator projection or EPSG:3857 as its projection. so in your code, you should put the center in EPSG:3857 coordinate system. If you want to use center coordinate in WGS 84 or lat-long numbers you should change the view projection to EPSG:4326 like this:
view: new ol.View({
center: [0,0],
projection: 'EPSG:4326',
zoom: 0
})
Projection is really important for further usages. If you have some data in a specific projection, Openlayers can't display them on a map with another projection. for example, if you have some data in WGS 84 you can't use them on the default view(cause it's Web Mercator).
So before choosing the view projection read about the limits. e.g. Basemaps are not available in every projection just like normal layers.
Also, there is another option you can use. Projecting the whole layer on the fly(in client browser) needs a massive process but we always can project a point or a single feature and use them among our layers or base map. In your case, you can only project your center point and use the map in Web Mercator. for this option, you need to use fromLonLat() like this:
center: ol.proj.fromLonLat([0,0])
Hope to be clear.
Try this one for your center:
center: ol.proj.fromLonLat([longitude,latitude])

LeafletJs only show one country

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>

Is there anyway to get a blank map with only imported data?

I'm an amateur web developer (by amateur I mean very low beginner level). I work at a Tour Operator and we recently started implementing the Google Maps API into our pages so we can show map location of lodges, hotels etc.
Unfortunately, most are already listed through Google and anyone is able to click the "View in Google Maps" option and get direct contact details (which is what we're trying to avoid as we work on commission.)
Is there any way to get a blank map so it only displays the points we plot?
If it helps, I'm currently using the Marker with Label javascript with the API
You mean is you need to Remove “labels” and all extra information from the map right.
you may go with adding some styles to your google map script.
here is full code for map withour information.
it may help.
you can change the colors and other styles if you needs
<!DOCTYPE html>
<html>
<head>
<title>Google map test demo</title>
<style type="text/css">
/* Set a size for our map container, the Google Map will take up 100% of this container */
#map {
width: 750px;
height: 500px;
}
</style>
<!--
You need to include this script tag on any page that has a Google Map.
The following script tag will work when opening this example locally on your computer.
But if you use this on a localhost server or a live website you will need to include an API key.
Sign up for one here (it's free for small usage):
https://developers.google.com/maps/documentation/javascript/tutorial#api_key
After you sign up, use the following script tag with YOUR_GOOGLE_API_KEY replaced with your actual key.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=YOUR_GOOGLE_API_KEY"></script>
-->
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js"></script>
<script type="text/javascript">
// When the window has finished loading create our google map below
google.maps.event.addDomListener(window, 'load', init);
function init() {
// Basic options for a simple Google Map
// For more options see: https://developers.google.com/maps/documentation/javascript/reference#MapOptions
var mapOptions = {
// How zoomed in you want the map to start at (always required)
zoom: 11,
// The latitude and longitude to center the map (always required)
center: new google.maps.LatLng(40.6700, -73.9400), // New York
// How you would like to style the map.
styles: [{"elementType":"labels.text","stylers":[{"visibility":"off"}]},{"featureType":"landscape.natural","elementType":"geometry.fill","stylers":[{"color":"#f5f5f2"},{"visibility":"on"}]},{"featureType":"administrative","stylers":[{"visibility":"off"}]},{"featureType":"transit","stylers":[{"visibility":"off"}]},{"featureType":"poi.attraction","stylers":[{"visibility":"off"}]},{"featureType":"landscape.man_made","elementType":"geometry.fill","stylers":[{"color":"#ffffff"},{"visibility":"on"}]},{"featureType":"poi.business","stylers":[{"visibility":"off"}]},{"featureType":"poi.medical","stylers":[{"visibility":"off"}]},{"featureType":"poi.place_of_worship","stylers":[{"visibility":"off"}]},{"featureType":"poi.school","stylers":[{"visibility":"off"}]},{"featureType":"poi.sports_complex","stylers":[{"visibility":"off"}]},{"featureType":"road.highway","elementType":"geometry","stylers":[{"color":"#ffffff"},{"visibility":"simplified"}]},{"featureType":"road.arterial","stylers":[{"visibility":"simplified"},{"color":"#ffffff"}]},{"featureType":"road.highway","elementType":"labels.icon","stylers":[{"color":"#ffffff"},{"visibility":"off"}]},{"featureType":"road.highway","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","stylers":[{"color":"#ffffff"}]},{"featureType":"road.local","stylers":[{"color":"#ffffff"}]},{"featureType":"poi.park","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"poi","elementType":"labels.icon","stylers":[{"visibility":"off"}]},{"featureType":"water","stylers":[{"color":"#71c8d4"}]},{"featureType":"landscape","stylers":[{"color":"#e5e8e7"}]},{"featureType":"poi.park","stylers":[{"color":"#8ba129"}]},{"featureType":"road","stylers":[{"color":"#ffffff"}]},{"featureType":"poi.sports_complex","elementType":"geometry","stylers":[{"color":"#c7c7c7"},{"visibility":"off"}]},{"featureType":"water","stylers":[{"color":"#a0d3d3"}]},{"featureType":"poi.park","stylers":[{"color":"#91b65d"}]},{"featureType":"poi.park","stylers":[{"gamma":1.51}]},{"featureType":"road.local","stylers":[{"visibility":"off"}]},{"featureType":"road.local","elementType":"geometry","stylers":[{"visibility":"on"}]},{"featureType":"poi.government","elementType":"geometry","stylers":[{"visibility":"off"}]},{"featureType":"landscape","stylers":[{"visibility":"off"}]},{"featureType":"road","elementType":"labels","stylers":[{"visibility":"off"}]},{"featureType":"road.arterial","elementType":"geometry","stylers":[{"visibility":"simplified"}]},{"featureType":"road.local","stylers":[{"visibility":"simplified"}]},{"featureType":"road"},{"featureType":"road"},{},{"featureType":"road.highway"}]
};
// Get the HTML DOM element that will contain your map
// We are using a div with id="map" seen below in the <body>
var mapElement = document.getElementById('map');
// Create the Google Map using our element and options defined above
var map = new google.maps.Map(mapElement, mapOptions);
// Let's also add a marker while we're at it
var marker = new google.maps.Marker({
position: new google.maps.LatLng(40.6700, -73.9400),
map: map,
title: 'test!'
});
}
</script>
</head>
<body>
<!-- The element that will contain our Google Map. This is used in both the Javascript and CSS above. -->
<div id="map"></div>
</body>

Incomplete map comes back from tile server, more tiles available at zoom level than docs indicate

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>

How to embed a Leaflet map into a Reveal.js presentation?

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
}
} );

Categories