Клип Google Maps JS API ImageMapType на полигон
Как я могу обрезать Тип карты в Google Maps на произвольный полигон. Например, если у меня есть заказ ImageMapType который охватывает большую площадь (т. е. весь мир), но я хочу показать его только внутри данного полигона (т. е. одной страны).
есть ли способ обрезать ImageMapType к заданному полигону или реализовать пользовательский MapType для достижения такого поведения? Он должен позволять масштабирование и панорамирование в обычном режиме.
остальная часть карты должна оставаться то же самое, и там будет тип карты, охватывающий только определенную область. Поэтому невозможно просто наложить полигон, чтобы покрыть области за пределами полигона, чтобы отобразить только то, что необходимо.
вот так:
отсечение на стороне сервера не является опцией.
6 ответов:
Я написал код для типа карты наложения, который делает то, что вы хотите. Будьте уверены, чтобы проверить в вашей целевой браузеры. Скрипка
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>
как это работает
в принципе
ClipMapType
класс-это интерфейс MapType.getTile
метод этого интерфейса вызывается с координатами плитки и уровнем масштабирования, чтобы получить плитку для каждой плитки.ClipMapType
создает элемент canvas в качестве плитки и рисует на плитке изображение обрезается внутри полигона. Если производительность важна, ее можно оптимизировать для более быстрой работы.отказ от ответственности
использование серверов Google tile путем взлома URL, вероятно, нарушает условия обслуживания Google Maps. Я использовал его для демонстрации и не рекомендую использовать его в производстве. Мой ответ-это попытка дать вам понимание, чтобы вы могли создать свое собственное решение.
вам нужны карты Google perse? Я знаю, что Openlayers 3 обеспечивает лучшую поддержку для такого рода вещей. Например, взгляните на этой.
Если вы действительно должны использовать Google Maps, я предлагаю реализовать свой собственный MapType и генерировать плитки, необходимые для покрытия области полигона самостоятельно с помощью MapTiler. (MapTiler также создает пример реализации Google Maps для вас, так что это не должно быть слишком сложно.)
вы можете разместить DIV над вашей картой, с абсолютным позиционированием и высоким Z-индексом. затем примените маску полигона к этому DIV следующим образом:
-webkit-clip-path: polygon(0 0, 0 100%, 100% 0);
можно использовать
canvas.toDataURI()
опция в HTML5, чтобы получить url, который требуется для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; }
- для того чтобы установить и изменить размер изображения, чтобы исправить размер, используйте
canvas.drawImage()
- чтобы обрезать изображение с холста до любого непрямоугольного размера, используйте
canvas clip()
пример кода для обрезки холста.
Я вижу, что вы не можете использовать обычные стратегии маскировки, потому что вам нужно видеть нижний слой. Могу ли я предложить более полный комплект обрезки SVG? посмотреть здесь.
совместимость с браузером хороша, но не велика, но вы можете абсолютно выполнить то, что вы пытаетесь здесь (Если вам не нужно панорамировать/масштабировать карту, то вы ввернуты, пока карты не реализуют такую вещь).
вы можете использовать SVG clippath вместе с тегом foreignobject svg, чтобы поместить html-документ в svg, а затем обрезать его до нужной формы, как этот код, взятый из 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>