How can I overlay an XYZ tile set (something like this) on Google Maps API 3? I want to overlay weather data (cloud cover...etc). Feel free to use my OpenWeatherMaps URL to test it out:
http://maps.owm.io:8091/56ce0fcd4376d3010038aaa8/{z}/{x}/{y}?hash=5
I have spent multiple days trying to figure out this seemingly simple feature.
If someone can provide a working example I would be in your debt. Feel free to check out my GitHub Gist implementation using OL3 and OSM of this weather data overlay. I'd also love to know if this is not easily achievable/requires hacks.
Thank you!
Update: Thanks to #wf9a5m75's answer, I was able to put together this jsFiddle with the solution to my problem: https://jsfiddle.net/601oqwq2/4/
ImageMapType is for your purpose.
Read here: https://developers.google.com/maps/documentation/javascript/maptypes#ImageMapTypes
var myMapType = new google.maps.ImageMapType({
getTileUrl: function(coord, zoom) {
return "http://maps.owm.io:8091/56ce0fcd4376d3010038aaa8/" +
zoom + "/" + coord.x + "/" + coord.y + "?hash=5";
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 9,
minZoom: 0,
name: 'mymaptype'
});
map.mapTypes.set('mymaptype', myMapType);
map.setMapTypeId('mymaptype');
[update] Overlay the imageMapType above the current mapType
var map = new google.maps.Map(document.getElementById('map'), {
center: {lat: -34.397, lng: 150.644},
zoom: 8
});
var myMapType = new google.maps.ImageMapType({
getTileUrl: function(coord, zoom) {
return "http://maps.owm.io:8091/56ce0fcd4376d3010038aaa8/" +
zoom + "/" + coord.x + "/" + coord.y + "?hash=5";
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 9,
minZoom: 0,
name: 'mymaptype'
});
map.overlayMapTypes.insertAt(0, myMapType);
Improving on wf9a5m75's answer.
The overlay image tiles don't cover the underlying map when zoomed out. We can make use of a normalization function (as mentioned in the link here) to ensure they cover the whole area.
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
#map {
height: 100%;
}
<body>
<div id="map"></div>
<script>
var map;
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
center: {
lat: 19.0356826,
lng: 72.9112641
},
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP,
disableDefaultUI: true
});
var myMapType = new google.maps.ImageMapType({
getTileUrl: function(coord, zoom) {
var normalizedCoord = getNormalizedCoord(coord, zoom);
if (!normalizedCoord) {
return null;
}
var bound = Math.pow(2, zoom);
return "http://maps.owm.io:8091/56ce0fcd4376d3010038aaa8/" +
zoom + "/" + normalizedCoord.x + "/" + (bound - normalizedCoord.y - 1) + "?hash=5";
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 8,
minZoom: 0,
name: 'mymaptype'
});
// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
var y = coord.y;
var x = coord.x;
// tile range in one direction range is dependent on zoom level
// 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
var tileRange = 1 << zoom;
// don't repeat across y-axis (vertically)
if (y < 0 || y >= tileRange) {
return null;
}
// repeat across x-axis
if (x < 0 || x >= tileRange) {
x = (x % tileRange + tileRange) % tileRange;
}
return {
x: x,
y: y
};
}
map.overlayMapTypes.insertAt(0, myMapType);
}
</script>
<script src="https://maps.googleapis.com/maps/api/js?callback=initMap" async defer></script>
</body>
Related
I am displaying an ImageMapType as overlay over a satellite mapType base map.
The base map seems to be limiting the max zoom to 19 in my area, if I change to a Road mapType I get a a couple of more levels. I have high resolution imaginery which I am displaying in the overlay and I want to be able to zoom further. If I use the ImageMapType as base map I can zoom in all I need, but I would really like to display the satellite base map and then continue to zoom into the image even if there's no satellite imaginery available.
Is there any way of accomplishing this? The only thing I can think of is creating a custom base map.
Code is similar to this example https://developers.google.com/maps/documentation/javascript/examples/maptype-image-overlay
Thanks!
My code:
var mapMinZoom = 10;
var mapMaxZoom = 25;
var zoom = 20;
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), {
center: new google.maps.LatLng(-34.567426059726316, -60.34467143006623),
zoom: zoom,
mapTypeId: google.maps.MapTypeId.SATELLITE,
panControl: false,
streetViewControl: false,
maxZoom: mapMaxZoom
});
createImageLayer('imaginery', 'images')
}
function createImageLayer(key, folder) {
var mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(-34.55771388565057, -60.367219001054764), new google.maps.LatLng(-34.55817334541287, -60.3209562599659));
var imageMapTypeLayer = new google.maps.ImageMapType({
minZoom: mapMinZoom,
maxZoom: mapMaxZoom,
name: key,
getTileUrl: function(coord, zoom) {
if ((zoom < mapMinZoom) || (zoom > mapMaxZoom)) {
return "http://www.maptiler.org/img/none.png";
}
var ymax = 1 << zoom;
var y = ymax - coord.y -1;
var tileBounds = new google.maps.LatLngBounds(
map.getProjection().fromPointToLatLng( new google.maps.Point( (coord.x), (coord.y) ) ),
map.getProjection().fromPointToLatLng( new google.maps.Point( (coord.x), (coord.y)))
);
// write a real condition here, so long this is always valid
if (tileBounds.intersects(tileBounds)) {
return "/static/ui/"+ folder + "/"+zoom+"/"+coord.x+"/"+y+".png";
} else {
return "http://www.maptiler.org/img/none.png";
}
},
tileSize: new google.maps.Size(256, 256)
});
map.overlayMapTypes.push(imageMapTypeLayer)
}
EDIT 1:
Up to now I have managed to overcome this by listening to the zoom_changed event and changing the base map to my imageMapType when the zoom reaches the maximum and back to satellite when the user zooms out. I wonder if there's a cleaner solution to this issue.
<!DOCTYPE html>
<html>
<head>
<title>Image map types</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
<script>
var moonTypeOptions = {
getTileUrl: function(coord, zoom) {
var bound = Math.pow(2, zoom);
return 'full-out' +
'/' + zoom + '/' + coord.x + '/' +
(bound - coord.y - 1) + '.png';
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 6,
minZoom: 1,
radius: 1738000,
name: 'Moon'
};
var moonMapType = new google.maps.ImageMapType(moonTypeOptions);
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var mapOptions = {
center: myLatlng,
zoom: 1,
streetViewControl: false,
mapTypeControlOptions: {
mapTypeIds: ['moon']
}
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
map.mapTypes.set('moon', moonMapType);
map.setMapTypeId('moon');
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Hello World!'
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
I have above code to render a custom large 16834 * 16834 image of 19 mb which is broken into tiles using gdal2tiles and gdal_translate. In short, I have all the corresponding tiles.
The above code is working perfectly fine rendering image and different zoom levels. However, when I add marker, it is displayed multiple times at lower zoom level. I would like the marker not repeat itself horizontally.
Is there any way to avoid horizontal repeating markers? Currently, I'm using Leaflet.js which doesn't repeat marker horizontally as Google Maps library.
I want to use Google maps because of its stability and popularity.
I'm using Ubuntu 14.04 as OS.
set the optimized-option of the markers to false
For those who has still this problem, have a look at my solution.
1- Set the maps zoom to (2) and add marker positions (lat,long) i.e
var minZoomLevel = 2;
map.setZoom(minZoomLevel);
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < result.length; i++){
var latlng = new google.maps.LatLng(result[i].Lat, result[i].Lng);
bounds.extend(latlng);
});
2- Attach a event listener on zoom changed i.e
google.maps.event.addListener(map, 'zoom_changed', function() {
if (map.getZoom() < minZoomLevel) map.setZoom(minZoomLevel);
});
3- Attach a center changed listener (This done the trick) i.e
google.maps.event.addListener(map, 'center_changed', function()
{
checkBounds(bounds);
}
function checkBounds(allowedBounds) {
if(allowedBounds.contains(map.getCenter())) {
return;
}
var mapCenter = map.getCenter();
var X = mapCenter.lng();
var Y = mapCenter.lat();
var AmaxX = allowedBounds.getNorthEast().lng();
var AmaxY = allowedBounds.getNorthEast().lat();
var AminX = allowedBounds.getSouthWest().lng();
var AminY = allowedBounds.getSouthWest().lat();
if (X < AminX) {X = AminX;}
if (X > AmaxX) {X = AmaxX;}
if (Y < AminY) {Y = AminY;}
if (Y > AmaxY) {Y = AmaxY;}
map.setCenter(new google.maps.LatLng(Y,X));
}
Every time you change the center, it will check your points and restrict map to certain area . Setting zoom will show only one world tile, and check bound will restrict the horizontal scrolling, thats how your markers will show only one time in map, set zoom according to your condition that fits in !!
I'm trying to make a custom map with markers.
I already got a custom map that work's but when i try to add a marker it results in a blank page.
i have no idea what im doing wrong because i did everything i should do, unless I missed something.
I used custom images that are public available
my correct code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1.0,user-scalable=no" />
<meta charset="utf-8" />
<title>Nexoness Nation - Google Maps</title>
<link rel="shortcut icon" href="https://maps.gstatic.com/favicon3.ico"/>
</head>
<body onload="initialize()">
<div id="map-canvas" style="width: 100%; height: 100%"></div>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var customMapTypeOptions = {
getTileUrl: function(coord, zoom) {
var normalizedCoord = getNormalizedCoord(coord, zoom);
if (!normalizedCoord) {
return null;
}
var bound = Math.pow(2, zoom);
/*Edit this URL to where you upload your tiles...*/
return "http://nexonessnation.bugs3.com/tile_" + zoom + "_" + normalizedCoord.x + "-" + normalizedCoord.y + ".svg";
},
tileSize: new google.maps.Size(256, 256),
isPng: true,
maxZoom: 3,
minZoom: 0,
name: "Nexoness Nation"
};
var customMapType = new google.maps.ImageMapType(customMapTypeOptions);
// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
var y = coord.y;
var x = coord.x;
// tile range in one direction range is dependent on zoom level
// 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
var tileRange = 8 << zoom;
// don't repeat across y-axis (vertically)
if (y < 0 || y >= tileRange) {
return null;
}
// repeat across x-axis
if (x < 0 || x >= tileRange) {
x = (x % tileRange + tileRange) % tileRange;
}
return {
x: x,
y: y
};
}
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var myOptions = {
zoom: 1,
center: myLatlng,
mapTypeControlOptions: {
mapTypeIds: ["Nexoness Nation"]
}
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
map.mapTypes.set('Nexoness Nation', customMapType);
map.setMapTypeId('Nexoness Nation');
}
function addMarkers() {
var bounds = map.getBounds();
var southWest = bounds.getSouthWest();
var northEast = bounds.getNorthEast();
var lngSpan = northEast.lng() - southWest.lng();
var latSpan = northEast.lat() - southWest.lat();
for (var i = 0; i < 10; i++) {
var latLng = new google.maps.LatLng(southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random());
var marker = new google.maps.Marker({
position: latLng,
map: map_canvas
});
}
}
</script>
</body>
</html>
Does anybody see what i'm doing wrong?
The ID of your div ("map-canvas") in <div id="map-canvas" style="width: 100%; height: 100%"></div> does not match the id you indicate in your script: map = new google.maps.Map(document.getElementById("**map_canvas**"), myOptions);
Also in the jsfiddle you provided, you need to select no wrap - <in body> instead of onLoad in the second dropdown on the left menu because you're calling your initialize() function in the onLoad of the body.
Update: indeed, I forgot about the markers. First the function addMarkers() wasn't called from initialize(). Also let's not forget to send the "map" as a parameter so we can use it in addMarkers.
Finally getBounds is available after the event bounds_changed is fired, we just need to add a listener on it to get the values.
Here is a jsfiddle that works:
http://jsfiddle.net/M2RD6/4/
I have a problem, when I using this code in custom maps from my server (below is example from google documentation, but my script is similar excluding the path to the map). Async loading not work for me:
<!DOCTYPE html>
<html>
<head>
<title>Image map types</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script>
var moonTypeOptions = {
getTileUrl: function(coord, zoom) {
var normalizedCoord = getNormalizedCoord(coord, zoom);
if (!normalizedCoord) {
return null;
}
var bound = Math.pow(2, zoom);
return 'http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' +
'/' + zoom + '/' + normalizedCoord.x + '/' +
(bound - normalizedCoord.y - 1) + '.jpg';
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 9,
minZoom: 0,
radius: 1738000,
name: 'Moon'
};
var moonMapType = new google.maps.ImageMapType(moonTypeOptions);
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var mapOptions = {
center: myLatlng,
zoom: 1,
streetViewControl: false,
mapTypeControlOptions: {
mapTypeIds: ['moon']
}
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
map.mapTypes.set('moon', moonMapType);
map.setMapTypeId('moon');
}
// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
var y = coord.y;
var x = coord.x;
// tile range in one direction range is dependent on zoom level
// 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
var tileRange = 1 << zoom;
// don't repeat across y-axis (vertically)
if (y < 0 || y >= tileRange) {
return null;
}
// repeat across x-axis
if (x < 0 || x >= tileRange) {
x = (x % tileRange + tileRange) % tileRange;
}
return {
x: x,
y: y
};
}
google.maps.event.addDomListener(window, 'load', initialize);
function loadScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://maps.googleapis.com/maps/api/js?sensor=false&' +
'callback=initialize';
document.body.appendChild(script);
}
window.onload = loadScript;
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
My site is blank and console show me:
Uncaught ReferenceError: google is not defined
But when I using this code:
<!DOCTYPE html>
<html>
<head>
<title>Image map types</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script>
function loadScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://maps.googleapis.com/maps/api/js?sensor=false&' +
'callback=initialize';
document.body.appendChild(script);
}
window.onload = loadScript;
var moonTypeOptions = {
getTileUrl: function(coord, zoom) {
var normalizedCoord = getNormalizedCoord(coord, zoom);
if (!normalizedCoord) {
return null;
}
var bound = Math.pow(2, zoom);
return 'http://mw1.google.com/mw-planetary/lunar/lunarmaps_v1/clem_bw' +
'/' + zoom + '/' + normalizedCoord.x + '/' +
(bound - normalizedCoord.y - 1) + '.jpg';
},
tileSize: new google.maps.Size(256, 256),
maxZoom: 9,
minZoom: 0,
radius: 1738000,
name: 'Moon'
};
var moonMapType = new google.maps.ImageMapType(moonTypeOptions);
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var mapOptions = {
center: myLatlng,
zoom: 1,
streetViewControl: false,
mapTypeControlOptions: {
mapTypeIds: ['moon']
}
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
map.mapTypes.set('moon', moonMapType);
map.setMapTypeId('moon');
}
// Normalizes the coords that tiles repeat across the x axis (horizontally)
// like the standard Google map tiles.
function getNormalizedCoord(coord, zoom) {
var y = coord.y;
var x = coord.x;
// tile range in one direction range is dependent on zoom level
// 0 = 1 tile, 1 = 2 tiles, 2 = 4 tiles, 3 = 8 tiles, etc
var tileRange = 1 << zoom;
// don't repeat across y-axis (vertically)
if (y < 0 || y >= tileRange) {
return null;
}
// repeat across x-axis
if (x < 0 || x >= tileRange) {
x = (x % tileRange + tileRange) % tileRange;
}
return {
x: x,
y: y
};
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
Google maps are loaded on the page, but still shows me:
Uncaught ReferenceError: google is not defined
What could be the reason?
Any occurence of google in global scope(outside of a function) will force this error. The first time you may access google is in initialize.
is it possible to not let the markers repeat horizontally, since I only want the marker to be show on the map, and I am not repeating my map.
As you can see here, it is very anoying:
http://lsres.com/playerdb/test2.php
I currently initialize my map this way:
var map;
var mapTypeOptions = {
getTileUrl: function(coord, zoom) { if (coord.y < 0 || coord.y >= 1 << zoom || coord.x < 0 || coord.x >= 1 << zoom){ return "sam/map/samap_a.jpg"; } return "sam/map/samap_"+zoom+"_"+coord.x+"_"+coord.y+".jpg"; },
tileSize: new google.maps.Size(256, 256),
maxZoom: 2,
minZoom: 1,
name: "Map",
opacity: 1.0,
isPng: false,
alt: "Map"
};
var satTypeOptions = {
getTileUrl: function(coord, zoom) { if (coord.y < 0 || coord.y >= 1 << zoom || coord.x < 0 || coord.x >= 1 << zoom){ return "sam/map/samap_a.jpg"; } return "sam/sat/samap_"+zoom+"_"+coord.x+"_"+coord.y+".jpg"; },
tileSize: new google.maps.Size(256, 256),
maxZoom: 2,
minZoom: 1,
name: "Satelite"
};
var mapMapType = new google.maps.ImageMapType(mapTypeOptions);
var satMapType = new google.maps.ImageMapType(satTypeOptions);
function initialize() {
var myLatlng = new google.maps.LatLng(0, 0);
var myOptions = {
center: myLatlng,
zoom: 1,
streetViewControl: false,
panControl: false,
zoomControl: false,
scaleControl: false,
mapTypeControlOptions: {
mapTypeIds: ["map", "sat"]
}
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
map.mapTypes.set('map', mapMapType);
map.mapTypes.set('sat', satMapType);
map.setMapTypeId('map');
}
Thanks in advance!
When creating marker object, set optimized to false, for example:
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
title: 'Hello World!',
optimized: false
});
It's because at zoom levels 1 and 2, the map has to repeat to not have blank space. The same thing happens at maps.google.com. What you can do is use zoom level 3 and 4 instead. You'll have to change your code so that the content contains more blank blue filler, but the map will no longer repeat. And the map details will still look good. Does that make sense?
There's one technique that could help, it's from this website, "Range Limiting"
http://econym.org.uk/gmap/range.htm
It's in V2, so I put together a V3 jsfiddle here.
http://jsfiddle.net/44e6y/1/
You'll see the map will not display too far away from the initial point. That is, you cannot drag the map beyond predefined limits.
However, you'll have to shrink the viewable area if you choose this method (it does nothing to the repeating markers).