Snap the Google Maps JS ImageMapType API to a polygon

How to copy MapType in Google Maps to an arbitrary polygon. For example, if I have a custom ImageMapType that covers a large area (i.e. the whole world), but I want to show it only inside the given polygon (i.e. one country).

Is there a way to crop ImageMapType for a given polygon or implement a custom MapType to achieve this behavior? It should provide zooming and panning as usual.

The rest of the map should remain the same and there will be a MapType covering only a specific area. Therefore, it is impossible to simply overlay a polygon to cover areas outside the polygon to display only what is needed.

Same:

Australia map with a clipped overlay on South Australia

Server-side cropping is not an option.

+56
javascript google-maps google-maps-api-3
Jun 19 '14 at 16:11
source share
7 answers

I wrote code for an overlay map type that does what you want. Be sure to check 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, the ClipMapType class is the MapType interface. getTile method of this interface is called with tile coordinates and zoom level to get a tile for each tile. ClipMapType creates a canvas element to act like a tile and draws a tile image cropped inside the polygon. If productivity is important, it can be optimized to work faster.

Renouncement

Using Google tile servers by hacking a URL may violate the Google Maps Terms of Service. I used it for demonstration and do not recommend using it in production. My answer is an attempt to give you an idea of ​​how to create your own solution.

+11
Apr 21 '16 at 15:00
source share

Do you need Google Maps perse? I know that Openlayers 3 provides better support for these kinds of things. For example, see this .

If you really have to use Google Maps, I suggest implementing your own MapType and generating the tiles you need to cover your polygon area yourself using MapTiler . (MapTiler also creates an example implementation of Google Maps for you, so this should not be too complicated.)

+2
Feb 10 '16 at 12:07 on
source share

You can place a DIV above your card with absolute positioning and a high z index. then apply the polygon mask to this DIV as follows: -webkit-clip-path: polygon(0 0, 0 100%, 100% 0);

+1
Aug 04 '14 at 20:51
source share

I see that you cannot use the usual masking strategies because you need to see the lower level. Can I offer SVG a more complete set of compartments? See here .

Browser compatibility is good, but not great, but you can absolutely accomplish what you are trying here (if you do not need to pan / zoom the map, then you are screwed up until the Maps implement such a thing).

+1
Aug 29 '15 at 15:37
source share

You can use the canvas.toDataURI() parameter in HTML5 to get the URL that is required for getTileUrl() ImageMapType .

 getTileUrl: function(coord, zoom) { var normalizedCoord = getNormalizedCoord(coord, zoom); if (!normalizedCoord) { return null; } var bound = Math.pow(2, zoom); // reset and clip the preloaded image in a hidden canvas to your required height and width clippedImage = canvas.toDataURL(); return clippedImage; } 
  • To set and resize the image to adjust the size, use canvas.drawImage()
  • To copy an image from the canvas to any non-rectangular dimension, use canvas clip() Sample code to clip the canvas .
+1
Feb 16 '16 at 9:04 on
source share

You can use the svg clippath link along with the foreignobject svg tag to put the html document in svg and then copy it to the desired form, like this code taken from codepen.io/yoksel/pen/oggRwR:

 @import url(http://fonts.googleapis.com/css?family=Arvo:700); .svg { display: block; width: 853px; height: 480px; margin: 2em auto; } text { font: bold 5.3em/1 Arvo, Arial sans-serif; } 
 <svg class="svg"> <clippath id="cp-circle"> <circle r="180" cx="50%" cy="42%"></circle> <text text-anchor="middle" x="50%" y="98%" >Soldier Of Fortune</text> </clippath> <g clip-path="url(#cp-circle)"> <foreignObject width="853" x="0" y="0" height="480"> <body xmlns="http://www.w3.org/1999/xhtml"> <iframe width="853" height="480" src="//www.youtube.com/embed/RKrNdxiBW3Y" frameborder="0" allowfullscreen></iframe> </body> </foreignObject> </g> </svg> 

http://codepen.io/yoksel/pen/oggRwR

+1
Mar 29 '16 at 12:48
source share

Has anyone solved this problem?

0
Jun 19 '19 at 15:24
source share



All Articles