前言
一個多月沒有寫部落格了,今天嘗試着動筆寫點。
原因很多,最重要的原因是我轉行了。是的,我離開了開發崗位,走向了開發的天敵-産品經理。雖然名義上是産品經理,但是幹的事情也很雜,除了不寫代碼,其他的都幹,經常還要加個小班,是以就沒那麼多時間研究技術上的東西,機械鍵盤上已經落下了一層薄薄的灰塵。但是自己确實又愛碼農這一行,上班看着同僚暢快的敲着代碼,心裡就有點癢,是以下班沒事仍舊自己瞎捉摸,這不就總結出來今天這篇文章。
關于産品經理和研發的關系我還真得多說一句,雖然二者是天敵,但是一個懂研發的産品經理很容易和研發打交道,互相之間有很多的共同話題,每次項目來的時候我腦海中大概知道如何解決這件事情,甚至我還要幫項目對接的公司解決開發上的問題。。。
話不多說,開始今天的主題,今天主要介紹如何将 Shp 檔案轉為 GeoJson,這在 QGIS、ArcGIS 等專業軟體中很容易實作,隻需要點個按鈕就行了,本文正是來研究這點個按鈕背後發生的故事。本文是在使用 GeoTrellis 中碰到的,是以仍舊歸入此部落格集中,當然其中的架構等也都是基于 GeoTrellis 的。
一、實作方式
1.1 理論分析
其實這個過程邏輯上比較簡單,首先将 Shp 檔案讀入記憶體,再分别讀出空間屬性和普通屬性,将二者組合起來按照 GeoJson 檔案的格式寫入即可。
1.2 具體實作
- 讀 Shp 檔案
隻需要一行代碼即可解決:
val datas = ShapeFileReader.readSimpleFeatures(path)
這是 GeoTrellis 封裝好的讀 Shp檔案的方法,但是此種方式存在一個問題,無法設定讀 Shp 檔案時的編碼方式,如果 Shp 檔案不是 UTF-8 編碼會存在亂碼的問題,簡單改造一下源碼即可實作:
val datas = {
val file = new File(path)
val shpDataStore = new ShapefileDataStore(path.getFileUrl())
shpDataStore.setCharset(Charset.forName(charsetName))
val ftItr = shpDataStore.getFeatureSource.getFeatures.features
try {
val simpleFeatures = mutable.ListBuffer[SimpleFeature]()
while (ftItr.hasNext) simpleFeatures += ftItr.next()
simpleFeatures
} finally {
ftItr.close
shpDataStore.dispose()
}
這樣就能夠讀出 Shp 檔案中的所有内容,空間屬性附帶普通屬性,最終是 SimpleFeature 對象的集合。
- 将内容轉為 Feature
所謂 Feature 其實就是空間屬性和普通屬性的結合。
def parseAttribute(sf: SimpleFeature) = {
import scala.collection.JavaConversions._
sf.getProperties.drop(1).map { p =>
val attr = sf.getAttribute(p.getName)
p.getName.toString -> (if (attr == null) "" else attr.toString)
}.toMap
}
def getGeometryFromSimpleFeature(sf: SimpleFeature) = {
val original = sf.getDefaultGeometryProperty.getValue
Geometry(original.asInstanceOf[com.vividsolutions.jts.geom.Geometry])
}
def toFeature(sf: SimpleFeature) = {
Feature(getGeometryFromSimpleFeature(sf), parseAttribute(sf))
}
以上方法能将單個 SimpleFeature 對象轉為 Feature 對象,整個集合隻需要實作一下 map 方法即可。
- 轉為 GeoJson
在上一步中,肯定有同學很好奇,為什麼要将從 Shp 中讀出的 SimpleFeature 對象轉為 Feature 對象,原因就在于有了 Feature 對象,我們就可以很簡單的将其轉為 GeoJson。GeoTrellis 内置了一個将 Feature 集合轉為 GeoJson 的隐式方法,如下:
implicit class FeaturesToGeoJson[G <: Geometry, D: JsonWriter](features: Traversable[Feature[G, D]]) {
def toGeoJson(): String = {
JsonFeatureCollection(features).toJson.compactPrint
}
}
是以可以直接将第二步中得到的 Feature 集合轉為 GeoJson,如下:
import geotrellis.vector.io.json.Implicits._
val geojson = features.toGeoJson()
最後隻需要将 geojson 對象寫入檔案即可。
二、換種姿勢
以上代碼看上去輕描淡寫,确實我折騰了好長時間才整出來。此處我又要插一句,有很多同學通過各種方式咨詢我關于 GeoTrellis 使用的問題,我在這裡要做一下聲明:
首先,我知道的基本都毫無保留的寫在部落格裡了,關于技術點問我和看我的部落格差不多,我希望與大家一起探讨實作思路等方面的問題;
其次,就算問我,你是不是也得把問題描述清楚,有些同學截幾句代碼就要問我為什麼跑不通,對不起,首先我不知道你從哪篇文章中截出的,其次我也不知道你要做什麼事情,是以我真的無法回答;
第三,文章中的代碼都是針對當時 GeoTrellis 版本而言的,你看到的時候可能已經更新了,可能會存在跑不通的情況,但是一般不存在,并且幾時存在簡單修改一下應該就可以,并且無需轉牛角尖的問我類似 1TB Tiff 如何處理的問題,我的文章隻提供技術方法,不做科學研究。
當然我非常感謝大家對我的認同和支援,這也是我持續寫此部落格集的動力之一(以後可能越來越少了,産品。。。都是産品惹的禍)。
言歸正傳,當我用 Scala 折騰了幾天出來之後,一拍腦袋,不禁要罵自己幾句,為什麼我不直接拿 python 實作此功能呢?趕緊研究了一下,發現幾行代碼就搞定:
from geopandas import *
shpdata = GeoDataFrame.from_file(path)
features = [shpdata.__geo_interface__]
from json import dumps
geojson = open("demo.json", "w")
geojson.write(dumps({"type": "FeatureCollection", \
"features": features}, indent=2) + "\n")
geojson.close()
具體思路同上面分析,此處不具體展開,感興趣的可以自行研究。
就像前面看到的一篇文章中講到的,強大的不是 python 而是庫,是的,一種語言用的人多了,自然庫也就豐富了,而我們普通人可以選擇為自己喜歡的語言貢獻庫,同時我們也不能死守一種語言,應該具體問題具體分析,找到最适合解決問題的語言,當然每一件事情都是學習的過程,都不會白費。
三、總結
本文介紹了兩種語言下實作 Shp 轉為 GeoJson 的方式,主要是分析解決問題的思路。
Geotrellis系列文章連結位址http://www.cnblogs.com/shoufengwei/p/5619419.html
作者:魏守峰
公司:武漢一格空間科技有限公司
産品:流程化表格資料處理平台
出處:http://www.cnblogs.com/shoufengwei/
本文版權歸作者和部落格園共有,歡迎轉載、交流,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。如果覺得本文對您有益,歡迎點贊、歡迎探讨。