Skip to content. | Skip to navigation

Personal tools

Navigation

You are here: Home / Tips / Maps and GeoSpatial Packages

Maps and GeoSpatial Packages

安裝 GIS 功能的擴充模組之後,使用者能在地圖上選定座標資料,為內容項目標記地理資訊。

Node.js GeoCoding Leaflet example: microworldtour.github.io

最簡化的 Google Map 範例,2017 Feb 測試 github checkout 版本成功在 Plone5 環境執行,至少要把 Google Map 底圖取消,避免底圖無法顯示的問題,2018 Jun 測試 github checkout 版本遇到 Edit 地圖畫面沒出現的問題,包括沒出現 Marker 位置的功能畫面。也有人考量 collective.geo 太過複雜,想先嘗試輕量方案,或直接參考 Google Maps Embed APIStreet View 技術文件。

c.g.kml 可以把 Folder 或 Collection 的座標顯示在地圖。透過 c.g.behaviour 和c.g.geographer 和 c.g.mapwidget 合作能讓 Dexterity 具備功能。

API Key 的設定管理,先登入 Developer Console,從 API Manager 可以設定 IP 或 Domain 甚至 Port Number,範例像 140.109.100.10:8080/*。從 2016 / 06 之後,會出現 MissingKeyMapError 訊息,可在 Control Panel 欄位裡設定。

This page didn't load google maps correctly 2016/06/22 開始要求 API key

CARTO: turning location data into business outcomes

Managing Georeferenced Content with Plone and collective.geo

Makina Corpus Demo Video

tecnoteca.googlemap GDAL: install in virtualenv

Location Attribute as Default Value for Geocoding

Map Your Google Location History

WMS: 1) GeoServer without the CORS restriction 2) MapServer with the CORS restriction

WFS: 1) GeoServer with the CORS restriction, EPSG: 3857, topp:states osm:water_areas 2) MapServer with the CORS restriction, EPSG: 4326, cites continents

Plone Map

collective.geo 又稱為 Plone Maps,是廣被使用的擴充模組,原則上,同時適用於 Archetypes 或 Dexterity 型別。可參考 Giorgio Borelli 詳細介紹

Installation

首先,作業系統裡要安裝 geos 函式庫,以 Ubuntu 為例,包括 libgeos、libgeos-c1、libgeos-dev 等套件檔案,以 CentOS 為例,包括 geos geos-devel 等套件檔案,版本要在 3.x 以上,不然會遇到 undefined symbol: GEOSSimplify 錯誤訊息。

接著要執行 Buildout 設定方式分成簡易和進階兩種: 最簡易的方式是在 eggs 選項裡加上 collective.geo.bundle 就行。因為 Plone5 預設支援 Dexterity,這種方式會自動安裝 collective.geo.behaviour,在 Plone 4.3.x 搭配 Dexterity 型別的環境下,就要額外確保 collective.geo.behaviour 如實安裝。

Buildout 過程會安裝 BeautifulSoup 和 geopy 相依模組。geopy 在 0.95.x 昇級 0.96.x 之際,把 geocoders/google.py 移除,造成 collective.geo.mapwidget 2.0 的 from geopy.geocoders.google import GQueryError 載入失敗,新版已修訂這問題

collective.geo.* 包含多個相關套件,核心套件像是 collective.geo.geographer、collective.geo.openlayers、collective.geo.settings、collective.z3cform.mapwidget 擴充套件像是 collective.geo.usersmap、collective.geo.flexitopic。如果不想個別指定套件細節,可以只安裝 collective.geo.bundle,它會自動包含所有套件。編輯 buildout.cfg 安裝 collective.geo.bundle 的步驟,不同 Plone 版本之間,有些細節要注意:

啟用方式的詢問 collective.geo.contentlocations P5 collective.geo.kml collective.geo.bundle

通常會搭配 Dexterity 型別,所以要同時安裝 collective.geo.behaviour 模組。在 collective.geo.bundle 2.0 之後,只要指定 [dexterity] 參數,就會自動安裝 collective.geo.behaviour 模組,還有其他相依模組

eggs =
    collective.geo.bundle [dexterity]

註: 在 collective.geo.bundle 2.0 之前的版本,如果指定 [dexterity] 參數,反而會遇到 UnknownExtra: collective.geo.bundle 0.1 has no such extra feature 'dexterity' 錯誤。這時候,可以在 [sources] 區段指定程式碼來源:

[sources]
collective.geo.bundle = git https://github.com/collective/collective.geo.bundle.git

待確認: Plone 4.3.6 + Geo 2.3 設定值 googlemaps 為 [] 造成 default_layers 沒有值而產生錯誤

Plone 4.3.15 | BeautifulSoup 3.2.1 geopy 1.11.0 pygeoif 0.7 collective.z3cform.mapwidget 2.1 collective.geo.settings 3.1 collective.geo.openlayers 3.1 collective.geo.mapwidget 2.4 collective.geo.kml 3.3 collective.geo.geographer 2.1 collective.geo.contentlocations 3.1 collective.geo.bundle 2.3 collective.geo.behaviour 1.2

最簡單的方式,是編輯 buildout.cfg 內容,設定 eggs 參數值,加上 collective.geo.bundle 這一行。

eggs =
    collective.geo.bundle

編輯 buildout.cfg 內容,設定 extends 和 eggs 參數值。

extends =
    base.cfg
    versions.cfg
    http://good-py.appspot.com/release/plone.app.z3cform/0.5.3-1

eggs =
    collective.geo.bundle

編輯 buildout.cfg 後,就是執行 bin/buildout 讓設定值生效。

$ bin/buildout

再執行 bin/plonectl fg 啟動系統,觀察一切是否正常。

參考 http://www.openfoundry.org/en/foss-programs/8294--plone-2- 的說明。

z3c.form 要指定 3.3.4,OrderedSelect Widget 需要被處理。

進階安裝

搭配 plone.app.contenttypes 的場合裡,需要 collective.geo.kml 3.2 以上的版本。

在 src 目錄下載來自 github 的程式碼:

import os

lines = """
https://github.com/collective/collective.geo.bundle.git
https://github.com/collective/collective.geo.contentlocations.git
https://github.com/collective/collective.geo.kml.git
https://github.com/collective/collective.geo.mapwidget.git
https://github.com/collective/collective.geo.geographer.git
https://github.com/collective/collective.geo.openlayers.git
https://github.com/collective/collective.geo.settings.git
https://github.com/collective/collective.geo.behaviour.git
https://github.com/collective/collective.geo.polymaps.git
https://github.com/collective/collective.z3cform.mapwidget.git
"""

for line in lines.split('\n'):
    if line == '': continue
    cmd = 'git clone ' + line
    os.system(cmd)

要讓內容型別支援地理資訊功能,要結合 collective.geo.geographer (IAttributeAnnotatable 和 IGeoreferenceable) 的設定。特別注意 collective.geo.behaviour 模組,它用於搭配 Dexterity 型別的場合,早期版本只出現在 github 上,到 1.0b1 之後才釋出在 pypi 上。

shapely 不再是直接的相依模組

>>> geo = IGeoManager(site['dx-item'])
>>> geo.isGeoreferenceable()
True
>>> geo.setCoordinates('Point', (20.34, 25.02))

Plone 4.3.4 + Dexterity:

collective.geo.bundle           2.2    2.3
Products.AdvancedQuery        3.0.3
collective.geo.behaviour        1.1    1.2
collective.geo.kml              3.2    3.2
collective.geo.contentlocations 3.1    3.1
collective.geo.mapwidget      2.1.3    2.4
BeautifulSoup                 3.2.1
collective.z3cform.mapwidget    2.0    2.1
collective.geo.geographer       2.0    2.0
pygeoif                       0.4.1    0.6
collective.z3cform.colorpicker  1.1    1.4
collective.geo.settings         3.0    3.1
collective.geo.openlayers       3.1    3.1
geopy                        1.10.0 1.11.0

Docker: hub github

啟用與設定

在 Site Setup 的 Add-on Configuration 進入 Collective Geo 項目,在 Geo Settings 指定設定值。

取消 OpenLayer Google MapData Box 顯示

Google Maps Layer Copyright Popup

使用 WKT 格式,範例像 POINT (120.160070888419 23.0005158959299); 在 console 查詢時依版本可能型式不同 {'type': 'Point', 'coordinates': (Decimal('121.736848'), Decimal('25.122343'))} 和 {'type': 'Point', 'coordinates': (121.74403527116118, 25.12723071353583)}

from collective.geo.geographer.interfaces import IGeoreferenced
geo = IGeoreferenced(obj)
geo.type
geo.coordinates
geo.crs
geo.hasCoordinates()

zgeo_geometry Index 批次更新座標資料範例

geo = IWriteGeoreferenced(obj)
geo.setGeoInterface('Point', (float(x), float(y))
obj.reindexObject(idxs=['zgeo_geometry'])

MapWidget

map-widget Macro 定義 <div class="widget-cgmap"> 的顯示方式,並載入 Layer 設定值,包括地圖中心點,預設縮放級數,語系。

collectivegeo_init.js 會搜尋 widget-cgmap Class 元素,來啟動地圖顯示。

語系檔案在 Macro 裡會自動載入:

<metal:use use-macro="context/@@collectivegeo-macros/openlayers" />

透過 collective.geo.settings 可以設定參數,讓地圖呈現不同樣貌。

指定 Layer 的範例:

>>> from collective.geo.mapwidget.maplayers import BingRoadsMaplayer
>>> mw1._layers = [BingRoadsMapLayer(context=portal, request=request)]
>>> view = TestView(portal, request)
>>> setTemplate(view, template)
>>> view.mapfields = [mw1]
>>> print view()

指定 Image 底圖的範例:

from collective.geo.mapwidget.maplayers import MapLayer
class ImageLayer(MapLayer):
    name = "imagelayer"
    @property
    def jsfactory(self):
        return """function() {return new OpenLayers.Layer.Image('%s', {url: '%s'});}""" % \
(self.context.Title(), self.context.absolute_url())

延伸應用

像 leaflet 或 polymaps 這類 web mapping client 只接受 GeoJSON 資料,透過 collective.js.leafletcollective.geo.polymaps 再搭配 collective.geo.json,就可以建置 GeoJSON 資料的地圖應用。

PostGIS 相關套件: postgresql postgresql-server-dev postgis

匯出資料到 PostgreSQL WFS WMS

collective.geo.mapbox

collective.geo.mapview

develop.cfg

[sources]
collective.geo.mapview = git git@github.com:l34marr/collective.geo.mapview.git

[buildout]
eggs +=

myproj.content/implements.zcml

<configure
  xmlns="http://namespaces.zope.org/zope"
  xmlns:zcml="http://namespaces.zope.org/zcml"
  xmlns:i18n="http://namespaces.zope.org/i18n"
  xmlns:plone="http://namespaces.zope.org/plone">

<class class=".mytype.MyType" zcml:condition="installed collective.geo.geographer" <implements interface="collective.geo.geographer.interfaces.IGeoreferenceable" /> </class>

</configure>

collective.geo.openlayers 預設要 Plone >= 5.0,在此之前的 Plone 版本要搭配 4.x 之前的版本。

Migration

KeyError: 'Interface `collective.geo.settings.interfaces.IGeoSettings` defines a field `map_viewlet_managers`

要從 portal_setup 的 Upgrades 頁籤選 collective.geo.settings:default 按 Choose Profile 再按 Upgrade

其他參考資料

MapWidget 載入效能改善

leaflet 網站範例

shapely 相依關係 Rasterio Performance

collective.geo.geographer 的 IGeoCoder Utility 搬到 collective.geo.mapwidget

PleiadesGeocoder

比較兩個地理區域大小的工具

Python Protocol for Geospatial Data

註:如果遇到 Broken Products 提及 collective.geo.bundle 可以 git 存取 collective.z3cform.colorpicker 原始碼 參考 buildout.cfg 範例

註:如果遇到 ImportError: No module named schema.vocabulary 訊息,要把程式碼裡的 zope.app.schema.vocabulary 改成 zope.schema.interfaces,請在 buildout-cache/eggs 目錄裡用 grep -r zope.app.schema.vocabulary . | grep geo 找,說明文件可參考 http://marrtw.blogspot.com/2011/08/vocabulary-import-tips.html

collective.geo.index 相依於 Rtree http://pypi.python.org/pypi/Rtree
Rtree 的安裝方式還要查,取消 collective.geo.index 的安裝,看來並不影響 collective.geo 的正常安裝。
https://github.com/libspatialindex
空間索引的需求,來自兩個技術面向,一是提高快取的使用率,降低磁碟的存取,二是 Btree 不能對 GIS 資料進行有效的索引。

collective.geo.trackview 不再開發,其功能被併進 collective.geo.file 模組裡。

collective.cover collective.geo.usersmap

Google API Update

collective/geo/geographer/geocoder.py
@@ -23,9 +23,9 @@ class GeoCoderUtility(object):

     def retrieve(self, address=None, google_api=None):
         if google_api:
-            self.geocoder = geocoders.Google(str(google_api))
+            self.geocoder = geocoders.GoogleV3(str(google_api))
         else:
-            self.geocoder = geocoders.Google()
+            self.geocoder = geocoders.GoogleV3()

         if not address:
             raise GQueryError
@@ -58,7 +58,7 @@ class GeoCoderView(BrowserView):

     def __call__(self, address=None, google_api=None):
         try:
-            locations = self.geocoder.retrieve(address)
+            locations = self.geocoder.retrieve(address, google_api)
         except GQueryError:
             return 'null'
         return json.dumps([loc for loc in locations])

diff example/conference/configure.zcml

@@ -1,5 +1,6 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
+    xmlns:five="http://namespaces.zope.org/five"
     xmlns:grok="http://namespaces.zope.org/grok"
     xmlns:browser="http://namespaces.zope.org/browser"
     xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
@@ -22,6 +23,11 @@
         provides="Products.GenericSetup.interfaces.EXTENSION"
         />

+    <five:implements
+         class=".presenter.IPresenter"
+         interface="collective.geo.geographer.interfaces.IGeoreferenceab
+         />
+
     <!-- Special forms and views for Attendee, to be able to set
          widgets in the datagridfield.  We could maybe register them
          just for Attendees, but that would not help for the add-form.

develop.cfg

$ svn co http://svn.plone.org/svn/collective/collective.geo.behaviour/trunk collective.geo.behaviour
$ svn co http://svn.plone.org/svn/collective/collective.geo.bundle/trunk collective.geo.bundle
$ svn co http://svn.plone.org/svn/collective/collective.geo.contentlocations/trunk collective.geo.contentlocations
$ svn co http://svn.plone.org/svn/collective/collective.geo.file/trunk collective.geo.file
$ svn co http://svn.plone.org/svn/collective/collective.geo.flexitopic/trunk collective.geo.flexitopic
$ svn co http://svn.plone.org/svn/collective/collective.geo.geographer/trunk collective.geo.geographer
$ svn co http://svn.plone.org/svn/collective/collective.geo.geopoint/trunk collective.geo.geopoint
$ svn co http://svn.plone.org/svn/collective/collective.geo.index/trunk collective.geo.index
$ svn co http://svn.plone.org/svn/collective/collective.geo.json/trunk collective.geo.json
$ svn co http://svn.plone.org/svn/collective/collective.geo.kml/trunk collective.geo.kml
$ svn co http://svn.plone.org/svn/collective/collective.geo.mapcontent/trunk collective.geo.mapcontent
$ svn co http://svn.plone.org/svn/collective/collective.geo.mapwidget/trunk collective.geo.mapwidget
$ svn co http://svn.plone.org/svn/collective/collective.geo.openlayers/trunk collective.geo.openlayers
$ svn co http://svn.plone.org/svn/collective/collective.geo.opensearch/trunk collective.geo.opensearch
$ svn co http://svn.plone.org/svn/collective/collective.geo.polymaps/trunk collective.geo.polymaps
$ svn co http://svn.plone.org/svn/collective/collective.geo.settings/trunk collective.geo.settings
$ svn co http://svn.plone.org/svn/collective/collective.geo.trackview/trunk collective.geo.trackview
$ svn co http://svn.plone.org/svn/collective/collective.geo.xmlimport/trunk collective.geo.xmlimport
collective.geo.openlayers - skins/geo_openlayers/theme/default/style.css
collective.geo.mapwidget - skins/geo_openlayers/theme/default/google.css
collective.geo.kml - browser/kmldocument_macros.pt See the original resource
collective.geo.openlayers - skins/geo_openlayers/OpenLayers.js olPopupCloseBox
collective.geo.openlayers - browser/geo-openlayers.css .olPopupContent
collective.geo.kml - browser/kmldocument.py Placemarker
collective.geo.mapwidget - browser/configure.zcml collectivegeo-macros
*************** PICKED VERSIONS ****************
[versions]
BeautifulSoup = 3.2.1
Shapely = 1.2.14
collective.contentleadimage = 1.3.4
collective.geo.bundle = 0.1
collective.geo.contentlocations = 2.4
collective.geo.geographer = 1.4
collective.geo.kml = 2.4
collective.geo.mapwidget = 1.5
collective.geo.openlayers = 0.2.4
collective.geo.settings = 2.4
collective.z3cform.colorpicker = 0.2
geopy = 0.94.2

*************** /PICKED VERSIONS ***************

Miscellaneous

GeoPandas: Key Packages for Spatial Data Support Find Unique Id

unique_ids = df.groupby('id', as_index=False).first()
unique_ids['geometry'] = GeoSeries([Point(x, y) for x, y in zip(unique_ids['lon'], unique_ids['lat'])])

df.merge(unique_ids[['id', 'geometry']], how='left', on='id')

Docker: Geonode, Geoserver, Postgis

WKT to KML transformation: MySQL WKB

GDAL (Geospatial Data Abstraction Library)

GeoTiff 預設不建立 .aux.xml 檔案

gdal_translate -of HFA -co AUX=YES -co STATISTICS=YES xxx.tif xxx.aux
# Using GDAL to Open Image, by GeoSpatial Development by Example
import gdal

def open_image_gdal(img_path):
  dataset = gdal.Open(img_path)
  cols = dataset.RasterXSize
  rows = dataset.RasterYSize
  print "Image dimensions: {} x {}px".format(cols, rows)
  raw_input("Press any key.")

if __name__ == '__main__':
  image_path = 'some_file.tif'
  open_image_gdal(image_path)

Beginners Guide to parallel processing with Geoprocessing Analysis Tools

How to Fix "this page didn't load google maps correctly"

https://github.com/kthyng/python4geosciences/tree/master/examples

fishnet

QGIS

import Excel/CSV showing on Google Earth

Related content
Web Mapping Tools