天天看點

geotrellis使用(四十二)将 Shp 檔案轉為 GeoJson

前言

一個多月沒有寫部落格了,今天嘗試着動筆寫點。

原因很多,最重要的原因是我轉行了。是的,我離開了開發崗位,走向了開發的天敵-産品經理。雖然名義上是産品經理,但是幹的事情也很雜,除了不寫代碼,其他的都幹,經常還要加個小班,是以就沒那麼多時間研究技術上的東西,機械鍵盤上已經落下了一層薄薄的灰塵。但是自己确實又愛碼農這一行,上班看着同僚暢快的敲着代碼,心裡就有點癢,是以下班沒事仍舊自己瞎捉摸,這不就總結出來今天這篇文章。

關于産品經理和研發的關系我還真得多說一句,雖然二者是天敵,但是一個懂研發的産品經理很容易和研發打交道,互相之間有很多的共同話題,每次項目來的時候我腦海中大概知道如何解決這件事情,甚至我還要幫項目對接的公司解決開發上的問題。。。

話不多說,開始今天的主題,今天主要介紹如何将 Shp 檔案轉為 GeoJson,這在 QGIS、ArcGIS 等專業軟體中很容易實作,隻需要點個按鈕就行了,本文正是來研究這點個按鈕背後發生的故事。本文是在使用 GeoTrellis 中碰到的,是以仍舊歸入此部落格集中,當然其中的架構等也都是基于 GeoTrellis 的。

一、實作方式

1.1 理論分析

其實這個過程邏輯上比較簡單,首先将 Shp 檔案讀入記憶體,再分别讀出空間屬性和普通屬性,将二者組合起來按照 GeoJson 檔案的格式寫入即可。

1.2 具體實作

  1. 讀 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 對象的集合。

  1. 将内容轉為 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 方法即可。

  1. 轉為 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/

本文版權歸作者和部落格園共有,歡迎轉載、交流,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結。如果覺得本文對您有益,歡迎點贊、歡迎探讨。