Clip Google Maps JS API ImageMapType to a polygon

后端 未结 6 1697
梦毁少年i
梦毁少年i 2021-02-04 23:21

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 i

6条回答
  •  旧巷少年郎
    2021-02-05 00:08

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

    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.

提交回复
热议问题