小快 / 地图包
为 Symfony 5 提供谷歌地图和地理编码支持
v1.0.23
2024-01-31 09:42 UTC
Requires
- php: >=7.2.5
- ext-json: *
- doctrine/orm: ^2.7
- geocoder-php/google-maps-provider: ^4.5
- nyholm/psr7: ^1.5
- stof/doctrine-extensions-bundle: ^1.3
- symfony/form: ^5.3|^6.0
- symfony/framework-bundle: ^5.3|^6.0
- symfony/http-client: ^5.3|^6.0
- symfony/yaml: ^5.3|^6.0
- twig/extra-bundle: ^2.12|^3.0
- willdurand/geocoder-bundle: ^5.13
README
为 Symfony 5 提供谷歌地图和地理编码支持
安装
打开命令行,进入您的项目目录并执行以下命令以下载此包的最新稳定版本
$ composer require kikwik/gmap-bundle
配置
将您的 API 密钥添加到 .env 文件中
GMAP_API_KEY
用于服务器地理编码GMAP_API_KEY_JS
用于 JavaScript 地图
###> geocoder ### # https://console.cloud.google.com/apis/credentials?hl=it&project=my-project # credential: "My server api key" # allowed IP address: xxx.xxx.xxx.xxx | yyy.yyy.yyy.yyy GMAP_API_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx # https://console.cloud.google.com/apis/credentials?hl=it&project=my-project # credential: "My javascript api key" # allowed domains: https://*.my-domain.ltd/* | https://my-domain.ltd/* GMAP_API_KEY_JS=yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy ###< geocoder ###
自动装配
该包将配置一个具有签名 Provider $googleMapsGeocoder
的自动装配提供者,您可以使用如下方式使用它
namespace App\Service; use Geocoder\Provider\Provider; use Geocoder\Provider\GoogleMaps\Model\GoogleAddress; class MyService { private $googleMapsGeocoder; public function __construct(Provider $googleMapsGeocoder) { $this->googleMapsGeocoder = $googleMapsGeocoder; } public function doGeocode(string $address) { // create the GeocodeQuery with the address to geocode $geocodeQuery = GeocodeQuery::create('piazza Duomo 1, Milano') ->withLocale('it'); // ask geocode to the $googleMapsGeocoder provider, // the result is an array of GoogleAddress objects /** @var GoogleAddress $geocodeResults[] */ $geocodeResults = $googleMapsGeocoder->geocodeQuery($geocodeQuery); // first result should be the best match return $geocodeResults[0] ?? null; } }
可地理编码实体
使您的实体(和仓库)可地理编码,通过实现 GeocodableEntityInterface
(和 GeocodableRepositoryInterface
)
使用提供的特性来快速实现
namespace App\Entity; use Kikwik\GmapBundle\Geocodable\GeocodableEntityInterface; use Kikwik\GmapBundle\Geocodable\GeocodableEntityTrait; class Place implements GeocodableEntityInterface { use GeocodableEntityTrait; // ... }
namespace App\Repository; use Kikwik\GmapBundle\Geocodable\GeocodableRepositoryInterface; use Kikwik\GmapBundle\Geocodable\GeocodableRepositoryTrait; class PlaceRepository extends ServiceEntityRepository implements GeocodableRepositoryInterface { use GeocodableRepositoryTrait; // ... }
然后您可以将地址设置为实体,并请求对其进行地理编码(传递提供者)
namespace App\Controller; use Geocoder\Provider\Provider; class GmapController extends AbstractController { /** * @Route("/gmap/new", name="app_gmap_new") */ public function createNewPlace(Provider $googleMapsGeocoder, EntityManagerInterface $entityManager) { // create an object that implement GeocodableEntityInterface /** @var Kikwik\GmapBundle\Geocodable\GeocodableEntityInterface $place */ $place = new Place(); // fill the address fields $place->setStreet('Piazza Duomo'); $place->setStreetNumber('1'); $place->setZipCode('20100'); $place->setCity('Milano'); $place->setProvince('MI'); $place->setCountry('Italia'); // Ask geocode by passing the provider $place->doGeocode($googleMapsGeocoder); $entityManager->persist($place); $entityManager->flush(); return $this->redirect($place->getGmapsUrl()); } }
地理编码命令
使用 kikwik:gmap:geocode
命令,您可以批量地理编码所有需要地理编码的实体(从未地理编码或地址在最后一次地理编码后更改的实体)
$ php bin/console kikwik:gmap:geocode --limit=5
使用 --failed
选项尝试重新地理编码失败的实体
$ php bin/console kikwik:gmap:geocode --limit=5 --failed
显示地图
- 在 JavaScript 块中调用
kw_gmap_script_tags
twig 函数以初始化 GMap 库,最终传递可选的 nonce 值 - 然后创建一个新的
kwMap
对象 - 并调用其
init
函数,它返回一个解析为地图加载完成的 promise
{% block javascripts %} {{ parent() }} {{ kw_gmap_script_tags(csp_nonce('script')) }} <script> document.addEventListener("DOMContentLoaded", function() { let mapElements = document.querySelectorAll('.kw-map'); mapElements.forEach(function (mapElement){ let map = new kwMap(); map.init(mapElement) .then(function (){ map.getGMap().addListener('bounds_changed', function() { const searchText = document.getElementById('map-search-txt'); const searchResults = document.getElementById('search-results'); const searchResultList = searchResults.querySelector('.js-location-list'); const searchResultCount = searchResults.querySelector('.js-location-count'); if(searchText.value) { // get visible markers let visibleMarkers = map.getVisibleMarkers(); // update counter searchResultCount.textContent = '('+visibleMarkers.length+')'; // empty result list searchResultList.innerHTML = ''; // add results to list for(let visibleMarker of visibleMarkers) { let node = document.createElement('li'); node.id = 'result-'+visibleMarker.id; node.innerHTML = visibleMarker.info; searchResultList.appendChild(node); } // show results searchResults.classList.remove('d-none'); } else { // hide results searchResults.classList.add('d-none'); } }); const mapSourceRadios = document.querySelectorAll('.js-map-source'); mapSourceRadios.forEach(function (mapSourceRadio){ mapSourceRadio.addEventListener('click', function() { map.clearMarkers(); let url = this.value; mapElement.dataset.mapRemoteMarkers = url; map.loadMarkers(); }) }) }) }); }); </script> {% endblock %}
然后在页面上为每个地图放置一个 div,并使用 twig 辅助函数
{{ kw_map_data_center(-31.56391, 147.154312) }}
- 设置地图中心,参数是一对浮点数{{ kw_map_data_center(place) }}
- 设置地图中心,参数是一个 GeocodableEntityInterface 对象{{ kw_map_data_zoom(3) }}
- 设置地图缩放,参数是一个整数{{ kw_map_data_markers(places) }}
- 加载标记,参数是一个 GeocodableEntityInterface 对象数组{{ kw_map_data_cluster({ maxZoom: 10, minPoints: 5 }, 'darkgreen') }}
- 启用聚合功能,参数是一个 SuperCluster 选项数组,(见 https://github.com/mapbox/supercluster#options) 以及一个可选的颜色(这会激活 SingleColorRenderer){{ kw_map_data_remote_markers(asset('path/to/file.json')) }}
- 加载远程标记,参数是远程 URL{{ kw_map_data_search_address('#map-address','#map-address-submit', { findNearestMarker: true }) }}
- 绑定搜索表单,参数是输入文本的 CSS 选择器,提交按钮的 CSS 选择器以及一个选项数组{{ kw_map_data_street_view('#street-view',place) }}
- 启用街景,参数是容器的 CSS 选择器和一个 GeocodableEntityInterface 对象{{ kw_map_data_street_view('#street-view',41.9027835, 12.4963655) }}
- 启用街景,参数是容器的 CSS 选择器和一对浮点数
这里列出了所有支持的数据属性
data-map-center
一个表示 LatLngLiteral 的 JSON 字符串data-map-zoom
一个整数值data-map-markers
一个表示标记描述符数组的 JSON 字符串,每个标记描述符都必须有以下字段lat
纬度值(浮点数)lng
经度值(浮点数)info
Google.maps.InfoWindow 内容(可选)icon
图标文件(可选)identifier
识别标记的字符串(可选)
data-map-cluster
表示 SuperCluster 选项的 JSON 字符串(参见 https://github.com/mapbox/supercluster#options)data-map-cluster-color
表示集群的 SingleColorRenderer 颜色字符串data-map-remote-markers
从该 URL 加载 JSON 格式的标记data-map-search-address
用于定位地图的中心点的输入文本的 CSS 选择器data-map-search-submit
用于定位地图的提交按钮的 CSS 选择器data-map-search-find-nearest-marker
在成功搜索后设置为 "1",直到地图上出现标记时进行缩放data-map-street-view
将包含街景的元素的 CSS 选择器data-map-street-view-position
表示一个 LatLngLiteral 的 JSON 字符串
一些示例
<form> <input type="text" id="map-address" placeholder="Ricerca per città, indirizzo, CAP..."> <button type="submit" id="map-address-submit">Cerca ›</button> </form> Map with clustered external data and search box: <div class="ratio ratio-1x1"> <div class="kw-map" {{ kw_map_data_remote_markers(asset('agenzie.json')) }} {{ kw_map_data_cluster({ maxZoom: 10, minPoints: 10 }) }} {{ kw_map_data_search_address('#map-address','#map-address-submit', { findNearest: true }) }} ></div> </div> Empty map centered in australia: <div class="ratio ratio-1x1"> <div class="kw-map" {{ kw_map_data_center(-31.56391, 147.154312) }} ></div> </div> Empty map centered in place (an GeocodableEntityInterface object): <div class="ratio ratio-1x1"> <div class="kw-map" {{ kw_map_data_center(place) }} ></div> </div> Map with marker in all places (an array of GeocodableEntityInterface object): <div class="ratio ratio-1x1"> <div class="kw-map" {{ kw_map_data_markers(places) }}></div> </div> Map with zoom=3, marker in all places (an array of GeocodableEntityInterface object) centered in the first one, street view on the third one <div class="ratio ratio-1x1"> <div class="kw-map" {{ kw_map_data_center(places[0]) }} {{ kw_map_data_zoom(3) }} {{ kw_map_data_markers(places) }} {{ kw_map_data_search_address('#map-address','#map-address-submit') }} {{ kw_map_data_street_view('#street-view',places[2]) }} ></div> </div> <div class="ratio ratio-21x9"> <div id="street-view"></div> </div>
地址自动完成
在您的表单中使用 AddressAutocompleteType
来在单独的组件中地理编码地址
use Kikwik\GmapBundle\Form\Type\AddressAutocompleteType; class GmapController extends AbstractController { /** * @Route("/gmap/autocomplete", name="app_gmap_autocomplete") */ public function addressAutocomplete(Request $request) { $submittedData = null; $form = $this->createFormBuilder() ->add('indirizzo1',AddressAutocompleteType::class, [ ]) ->add('indirizzo2',AddressAutocompleteType::class, [ 'autocomplete_fields' => ['latitude','longitude'] ]) ->getForm(); $form->handleRequest($request); if($form->isSubmitted() && $form->isValid()) { $submittedData = $form->getData(); dump($submittedData['indirizzo1']['autocomplete']); dump($submittedData['indirizzo1']['street']); dump($submittedData['indirizzo1']['streetNumber']); dump($submittedData['indirizzo1']['zipCode']); dump($submittedData['indirizzo1']['locality']); dump($submittedData['indirizzo1']['city']); dump($submittedData['indirizzo1']['province']); dump($submittedData['indirizzo1']['region']); dump($submittedData['indirizzo1']['country']); dump($submittedData['indirizzo1']['latitude']); dump($submittedData['indirizzo1']['longitude']); } return $this->render('gmap/addressAutocomplete.html.twig',[ 'form'=>$form->createView(), 'submittedData' => $submittedData, ]); } }
请记住在您的模板中加载 gmap 脚本
{% block javascripts %} {{ parent() }} {{ kw_gmap_script_tags() }} {% endblock %}