How can I clip a MapType in Google Maps to an arbitrary polygon. For example, if I have a custom ImageMapType that covers a large area (i.e. all the world), but I want to show it only inside a given polygon (i.e. one country).
Is there a way to clip the ImageMapType to a given polygon, or to implement a custom MapType to achieve this behaviour? It should allow for zooming and panning normally.
The rest of the map should stay the same, and there would be a MapType covering only a specific area. Therefore, it is not possible to simply overlay a polygon to cover the areas outside the polygon to display just what is needed.
Like so:
Server-side clipping is not an option.
Your maps can now match your brand and style across your website and your apps! The Google Maps APIs now support you in creating beautiful styled maps for your Android and iOS apps as well as your website using the same JSON style object.
I have written the code for an overlay map type that does what you want. Be sure to test in your target browsers. Fiddle
function ClipMapType(polygon, map) { this.tileSize = new google.maps.Size(256, 256); this.polygon = polygon; this.map = map; } ClipMapType.prototype.getTile = function(coord, zoom, ownerDocument) { var map = this.map; var scale = Math.pow(2, zoom); if (coord.y < 0 || coord.y >= scale) return ownerDocument.createElement('div'); var tileX = ((coord.x % scale) + scale) % scale; var tileY = coord.y; // Your url pattern below var url = "https://khms0.google.com/kh/v=694&x=" + tileX + "&y=" + tileY + "&z=" + zoom; var image = new Image(); image.src = url; var canvas = ownerDocument.createElement('canvas'); canvas.width = this.tileSize.width; canvas.height = this.tileSize.height; var context = canvas.getContext('2d'); var xdif = coord.x * this.tileSize.width; var ydif = coord.y * this.tileSize.height; var ring = this.polygon.getArray()[0]; var points = ring.getArray().map(function(x) { var worldPoint = map.getProjection().fromLatLngToPoint(x); return new google.maps.Point((worldPoint.x) * scale - xdif, (worldPoint.y) * scale - ydif); }); image.onload = function() { context.beginPath(); context.moveTo(points[0].x, points[0].y); var count = points.length; for (var i = 0; i < count; i++) { context.lineTo(points[i].x, points[i].y); } context.lineTo(points[count - 1].x, points[count - 1].y); context.clip(); context.drawImage(image, 0, 0); context.closePath(); }; return canvas; }; function initMap() { var map = new google.maps.Map(document.getElementById('map'), { zoom: 4, center: { lat: 15, lng: 15 } }); var polygon = new google.maps.Data.Polygon([ [{ lat: 0, lng: 0 }, { lat: 30, lng: 30 }, { lat: 0, lng: 30 }] ]); var mapType = new ClipMapType(polygon, map); map.overlayMapTypes.insertAt(0, mapType); }
html, body { height: 100%; margin: 0; padding: 0; } #map { height: 100%; }
<div id="map"></div> <script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap"> </script>
How it works
Basically ClipMapType
class is a MapType interface. getTile
method of this interface is called with tile coordinates and zoom level to get tile for every tile. ClipMapType
creates a canvas element to act as a tile and draws the tile image clipped to inside of the polygon. If performance is important, it can be optimized to work faster.
Disclaimer
Usage of Google tile servers by hacking the URL, probably violates Google Maps Terms of Service. I used it for demonstration and don't recommend using it in production. My answer is an attempt to give you an insight for you to create your own solution.
Do you require Google Maps perse? I know Openlayers 3 provides better support for this kind of stuff. For example, take a look at this.
If you really must use Google Maps, I suggest implementing your own MapType and generate the tiles needed to cover your polygon area yourself using MapTiler. (MapTiler also generates an example Google Maps implementation for you, so that shouldn't be too hard.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With