Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clip Google Maps JS API ImageMapType to a polygon

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:

Australia map with a clipped overlay on South Australia

Server-side clipping is not an option.

like image 247
Nicolas Avatar asked Jun 19 '14 16:06

Nicolas


People also ask

Can you customize Google Maps API?

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.


2 Answers

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.

like image 104
Gokhan Kurt Avatar answered Oct 02 '22 09:10

Gokhan Kurt


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

like image 22
Danny Hoek Avatar answered Oct 02 '22 09:10

Danny Hoek