clesson-de/silverstripe-geocoding 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

clesson-de/silverstripe-geocoding

Composer 安装命令:

composer require clesson-de/silverstripe-geocoding

包简介

Geocoding, reverse geocoding and routing for Silverstripe CMS, backed by OpenStreetMap (Nominatim/OSRM) and Google Maps.

README 文档

README

Geocoding, reverse geocoding and routing for Silverstripe CMS 6.1, backed by OpenStreetMap (Nominatim / OSRM) and Google Maps.

Features

  • Geocoding — resolve a postal address to geographic coordinates (latitude / longitude)
  • Reverse geocoding — resolve coordinates back to a postal address
  • Routing — calculate a driving route from an origin through optional waypoints to a destination
  • Two built-in service backends — OpenStreetMap (free, no API key required) and Google Maps
  • Address model — standalone address records with international formatting via commerceguys/addressing
  • AddressType model — configurable address type taxonomy (e.g. invoice address, delivery address)
  • Region dropdown API — AJAX endpoint for dynamic region/state dropdowns based on the selected country
  • Automatic geocodingAddress records are geocoded automatically after each write (via AddressExtension)
  • SiteConfig tab — manage geocoding service records directly from the CMS Settings panel
  • Custom DBGeoCoordinate composite field — store latitude + longitude in a single $db declaration
  • Rate limiting — configurable per-service delay between API requests (required by Nominatim's usage policy)

Requirements

Dependency Version
PHP ^8.1
silverstripe/framework ^6
commerceguys/addressing ^2.1
guzzlehttp/guzzle ^7

Installation

composer require clesson-de/silverstripe-geocoding

After installation, run a database build:

/dev/build?flush=all

Address and AddressType models

This module provides two standalone models for address management that can be used independently of — or in combination with — the clesson-de/silverstripe-contacts module.

Address

Represents a physical address. Fields: Name, AddressLine1, AddressLine2, PostalCode, City, Region, CountryCode.

A formatted Summary string (localised using commerceguys/addressing) is generated automatically onBeforeWrite. An interactive map field showing the geocoded coordinates is added via AddressExtension.

AddressType

A configurable taxonomy for address types (e.g. invoice address, delivery address). Default types can be seeded in project config:

Clesson\Silverstripe\Geocoding\Models\AddressType:
  default_tags:
    invoice-address: 'Invoice address'
    delivery-address: 'Delivery address'

Region dropdown API

When displaying an Address edit form, the region field uses a dynamic dropdown. The module exposes a JSON endpoint that returns subdivision options for a given country:

GET /geocoding-api/address/regions?country=DE&locale=de

Requires CMS_ACCESS permission.

Configuration

Managing geocoding services in the CMS

  1. Log in to the CMS.
  2. Navigate to Settings (SiteConfig).
  3. Open the Geocoding tab.
  4. Select a map display service — choose which service provider (Google or OpenStreetMap) should render interactive maps in the CMS.
  5. Add geocoding services for each method (geocode, reverse geocode, route):
    • Use the Add button to link existing services or Add new to create a service inline.
    • Set the Quota (maximum calls per day; 0 = unlimited).
    • Drag services to change their priority order — the first service with available quota is used.
  6. Save the Settings page.

Creating a new geocoding service

  1. In the Geocoding tab, click Add new under any method section.
  2. Choose the service type: OpenStreetMap service or Google service.
  3. Fill in the form:
    • Name — a descriptive label for this service record.
    • Active — enables or disables the service.
    • API key — required for Google Maps only.
    • Base URL — overrides the default Nominatim URL (OpenStreetMap only; leave empty to use the public API).
    • OSRM base URL — overrides the default OSRM routing URL (OpenStreetMap only).
    • Rate limit (ms) — minimum wait time in milliseconds between two requests. Set to at least 1100 for Nominatim to comply with the Nominatim usage policy.
  4. Save the service.

User guide

Interactive map field

When editing an Address record in the CMS, you'll see an interactive map that displays the current coordinates (if set) and allows you to update them by clicking on the map:

  • Marker — appears if coordinates are already set. Drag the marker to update the coordinates.
  • Double-click — double-click anywhere on the map to place or move the marker.
  • Single-click — single-clicks allow you to pan the map without accidentally moving the marker.
  • Service — the map provider (Google Maps or OpenStreetMap) is determined by the Map display service setting in SiteConfig → Geocoding.

Frontend assets

This module includes JavaScript and CSS assets for the interactive map field. After installation, run:

composer vendor-expose

This exposes the compiled assets from client/admin/dist/ to public/_resources/silverstripe-geocoding/.

Developing frontend assets

If you need to modify the JavaScript or SCSS:

  1. Navigate to the module directory:

    cd silverstripe-geocoding
  2. Install Node.js dependencies (using the version specified in .nvmrc):

    nvm use
    npm install
  3. Build the assets:

    npm run build
  4. Or watch for changes during development:

    npm run watch

The .nvmrc file specifies the required Node.js version (22). Use nvm to switch to the correct version automatically.

Troubleshooting

Map shows as grey box / tiles don't load initially

Symptom: When you open a form with a MapField (especially in GridField detail forms or tabs), you see a grey box. After reloading the page, the map tiles load correctly.

Cause: The map container is loaded via AJAX, but the JavaScript initialization ran before the container appeared in the DOM.

Solution: This is automatically handled by the Entwine integration (map-entwine.js). Make sure you have:

  1. ✅ Run composer vendor-expose after installation
  2. ✅ Cleared the Silverstripe cache (?flush=all)
  3. ✅ Hard-refreshed your browser (Ctrl+Shift+R / Cmd+Shift+R)
  4. ✅ Checked the browser console for JavaScript errors

How it works: The module uses jQuery Entwine to watch for .geocoding-map-container elements. When a container appears in the DOM (including via AJAX), Entwine automatically initializes the map after a short delay (100ms) to ensure all assets are loaded.

Debug: Open the browser console and check:

// Should return 'object'
typeof window.GeocodingMapUtils

// Should show registered providers
console.log(window.GeocodingMapUtils)

// Check if Leaflet loaded (for OSM)
typeof L

// Check if Google Maps loaded
typeof google !== 'undefined' && google.maps

See Asset loading documentation for detailed debugging instructions.

Map doesn't appear at all

✓ Checklist:

  • Map display service selected in Settings → Geocoding?
  • Service is marked as Active?
  • API key configured (for Google Maps)?
  • composer vendor-expose executed?
  • Browser console shows no JavaScript errors?

Coordinates don't save

✓ Checklist:

  • Field name matches DB field name (e.g. 'GeoCoordinates')?
  • Hidden input fields are present in the DOM?
  • Form submitted successfully (check for validation errors)?

Coordinates are rounded / have fewer decimal places

Symptom: When you copy coordinates from Google Maps (e.g. 48.65129546574825, 9.28476328298257), they are saved with only 7 decimal places (e.g. 48.6512955, 9.2847633).

This is normal and intentional. The database uses DECIMAL(10,7) which provides 7 decimal places = ~1.1 cm accuracy. This is the industry standard for GPS coordinates and more than sufficient for address geocoding.

Google's 14+ decimal places (sub-micrometer precision) are only relevant for scientific/surveying applications. The difference is invisible on maps and has zero practical impact.

See the Coordinate precision guide for detailed explanation.

Developer documentation

Extensibility

This module is designed to be modular and extensible. You can add support for additional map providers (e.g. Bing Maps, Mapbox, HERE Maps) in your own module without modifying the core geocoding module.

How it works:

External Module                    Geocoding Module
┌─────────────────┐               ┌──────────────────────┐
│ BingService     │               │ GoogleService        │
│ (DataObject)    │               │ OpenStreetMapService │
└────────┬────────┘               └──────────┬───────────┘
         │                                   │
         │ extends                           │ extends
         ▼                                   ▼
┌─────────────────────────────────────────────────────────┐
│              GeocodingService                            │
│              (Abstract base model)                       │
└─────────────────────────────────────────────────────────┘

External Module                    Geocoding Module
┌─────────────────┐               ┌──────────────────────┐
│ BingMapProvider │               │ GoogleMapProvider    │
│                 │               │ OSMMapProvider       │
└────────┬────────┘               └──────────┬───────────┘
         │                                   │
         │ implements                        │ implements
         ▼                                   ▼
┌─────────────────────────────────────────────────────────┐
│           MapProviderInterface                           │
│  - getProviderKey()                                      │
│  - supports(GeocodingService)                            │
│  - getConfig() / getCSSResources() / getJSResources()    │
└───────────────────┬─────────────────────────────────────┘
                    │
                    │ registered via YAML
                    ▼
┌─────────────────────────────────────────────────────────┐
│           MapProviderRegistry                            │
│  (Central registry for all providers)                    │
└───────────────────┬─────────────────────────────────────┘
                    │
                    │ used by
                    ▼
┌─────────────────────────────────────────────────────────┐
│               MapField                                   │
│  (FormField that renders the interactive map)            │
└─────────────────────────────────────────────────────────┘

Documentation:

Using DBGeoCoordinate in your own DataObjects

Declare the field in $db using the GeoCoordinate type alias:

private static array $db = [
    'Location' => 'GeoCoordinate',
];

This creates two database columns: LocationLatitude DECIMAL(10,7) and LocationLongitude DECIMAL(10,7).

Note on precision: The 7 decimal places provide ~1.1 cm accuracy, which is the industry standard for GPS coordinates and more than sufficient for address geocoding. Services like Google Maps may return coordinates with 14+ decimal places, but the additional precision (sub-micrometer) is only relevant for scientific/surveying applications and will be automatically rounded when saved.

Add the MapField to your getCMSFields():

use Clesson\Silverstripe\Geocoding\Forms\MapField;

public function getCMSFields(): FieldList
{
    $fields = parent::getCMSFields();
    
    $fields->removeByName(['LocationLatitude', 'LocationLongitude']);
    
    /** @var MapField $locationField */
    $locationField = MapField::create('Location', $this->fieldLabel('Location'));
    $locationField->setValue($this->dbObject('Location'));
    
    // Optional: configure zoom levels
    $locationField->setZoomLevel(8);             // Default zoom when no marker (1-20)
    $locationField->setZoomLevelWithMarker(16);  // Zoom when marker exists (street-level)
    
    $fields->addFieldToTab('Root.Main', $locationField);
    
    return $fields;
}

Access the coordinate via the composite field object:

$location = $myRecord->dbObject('Location');

if ($location->exists()) {
    echo $location->getLatitude();   // float, e.g. 52.5163
    echo $location->getLongitude();  // float, e.g. 13.3777
    echo $location->Nice();          // "52.5163, 13.3777"
}

// Set values
$location->setLatitude(52.5163);
$location->setLongitude(13.3777);
$myRecord->write();

Using the service classes in code

Retrieve a configured service record and instantiate the matching service class:

use Clesson\Geocoding\Constants\GeocodingServiceType;
use Clesson\Geocoding\Models\GeocodingService;
use Clesson\Geocoding\Services\OpenStreetMapGeocodingService;
use Clesson\Geocoding\Services\GoogleGeocodingService;

// Load the first active OpenStreetMap service
$serviceRecord = GeocodingService::get()
    ->filter(['ServiceType' => GeocodingServiceType::OPEN_STREET_MAP, 'Active' => true])
    ->first();

$service = new OpenStreetMapGeocodingService($serviceRecord);

Geocoding (address → coordinates)

$coordinate = $service->geocode([
    'street'     => 'Unter den Linden 1',
    'city'       => 'Berlin',
    'postalCode' => '10117',
    'country'    => 'DE',
]);

if ($coordinate !== null) {
    echo $coordinate->getLatitude();   // 52.5163...
    echo $coordinate->getLongitude();  // 13.3777...
}

Reverse geocoding (coordinates → address)

$address = $service->reverseGeocode(52.5163, 13.3777);

if ($address !== null) {
    echo $address['street'];     // "Unter den Linden"
    echo $address['city'];       // "Berlin"
    echo $address['postalCode']; // "10117"
    echo $address['country'];    // "de"
}

Routing (start + waypoints + destination → route)

$result = $service->route(
    ['lat' => 52.5163, 'lng' => 13.3777],  // origin: Berlin
    ['lat' => 48.1351, 'lng' => 11.5820],  // destination: Munich
    [
        ['lat' => 50.9333, 'lng' => 6.9500], // waypoint: Cologne
    ]
);

if ($result !== null) {
    echo $result['distanceMeters'];   // total distance in meters
    echo $result['durationSeconds'];  // total duration in seconds
    print_r($result['steps']);        // array of route steps
}

License

BSD 3-Clause License — see LICENSE.

clesson-de/silverstripe-geocoding 适用场景与选型建议

clesson-de/silverstripe-geocoding 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 87 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 08 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「routing」 「geocoding」 「maps」 「silverstripe」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 clesson-de/silverstripe-geocoding 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 clesson-de/silverstripe-geocoding 我们能提供哪些服务?
定制开发 / 二次开发

基于 clesson-de/silverstripe-geocoding 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 87
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 59
  • 依赖项目数: 1
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: BSD-3-Clause
  • 更新时间: 2026-05-08