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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章