之前簡單介紹了geotrellis的工作過程以及一個簡單的demo,最近在此demo的基礎上實現了SRTM DEM數據的實時分析以及高程實時處理,下面我就以我實現的上述功能為例,簡單介紹一下geotrellis的數據處理過程。
一、原始數據處理
geotrellis支持geotiff的柵格數據(矢量數據還未研究),可以將geotiff直接緩存至hadoop框架下的Accumulo NOSQL數據庫,並建立金字塔等,具體處理過程在geotrellis.spark.etl.Etl類中。具體代碼如下:
1 def ingest[ 2 I: Component[?, ProjectedExtent]: TypeTag: ? => TilerKeyMethods[I, K], 3 K: SpatialComponent: Boundable: TypeTag, 4 V <: CellGrid: TypeTag: Stitcher: (? => TileReprojectMethods[V]): (? => CropMethods[V]): (? => TileMergeMethods[V]): (? => TilePrototypeMethods[V]) 5 ]( 6 args: Seq[String], keyIndexMethod: KeyIndexMethod[K], modules: Seq[TypedModule] = Etl.defaultModules 7 )(implicit sc: SparkContext) = { 8 implicit def classTagK = ClassTag(typeTag[K].mirror.runtimeClass(typeTag[K].tpe)).asInstanceOf[ClassTag[K]] 9 implicit def classTagV = ClassTag(typeTag[V].mirror.runtimeClass(typeTag[V].tpe)).asInstanceOf[ClassTag[V]] 10 11 /* parse command line arguments */ 12 val etl = Etl(args) 13 /* load source tiles using input module specified */ 14 val sourceTiles = etl.load[I, V] 15 /* perform the reprojection and mosaicing step to fit tiles to LayoutScheme specified */ 16 val (zoom, tiled) = etl.tile(sourceTiles) 17 /* save and optionally pyramid the mosaiced layer */ 18 etl.save[K, V](LayerId(etl.conf.layerName(), zoom), tiled, keyIndexMethod) 19
重要的就是參數args,geotrellis根據不同的參數將數據進行不同的處理。具體的參數信息在https://github.com/geotrellis/geotrellis/blob/master/docs/spark-etl/spark-etl-intro.md
中均有介紹,這里介紹一些重要的配置。
二、發起服務
要對外提供數據,系統首先要能夠發起服務,geotrellis建立一個服務也很容易,只需要使用以下語句系統遍自動的在host和相應的port上發起服務。
1 IO(Http) ! Http.Bind(service, host, port)
具體路由信息需要在service類中定義。service類需要繼承Actor方法,並覆蓋父類的receive方法。
1 override def receive = runRoute(serviceRoute) 2 3 def serviceRoute = get { 4 pathPrefix("gt") { 5 pathPrefix("tms")(tms) ~ 6 path("geoTiff")(geoTiff) 7 } ~ 8 pathEndOrSingleSlash { 9 getFromFile(staticPath + "/index.html") 10 } ~ 11 pathPrefix("") { 12 getFromDirectory(staticPath) 13 } 14 }
以上就是建立了service的路由匹配表以及具體的控制器。當只請求IP及相應端口時會請求index.html,請求gt/tms時交給tms控制器,gt/geotiff交給geotiff控制器,其他會去匹配靜態地址,如圖片、
js、css等。
三、瓦片調用
1 WOLayer = new L.tileLayer(server + 2 'gt/tms/{z}/{x}/{y}', { 3 format: 'image/png', 4 }); 5 WOLayer.addTo(map);
前台便會請求后台的tms控制器,tms控制器定義如下:
tms獲取到請求的x、y、z、值,並從Accumulo中取出相應的瓦片交給leaftlet,leaflet將瓦片數據放到合適的位置,便完成了瓦片的加載,從Accumulo中取出瓦片的的大致代碼如下:
1 val tile: Tile = tileReader.reader[SpatialKey, Tile](LayerId(LayerName, zoom)).read(key)
其中tileReader是一個AccumuloValueReader對象,很明顯看出此對象是一個有關Accumulo的對象,其中包含Accumulo的用戶密碼等。LayerName就是上文中導入數據時候設置的layer參數對應的值。key是個SpatialKey對象,val key = SpatialKey(x, y),記錄了瓦片x、y編號值。讀到瓦片之后將數據發送到前台的代碼如下:
1 respondWithMediaType(MediaTypes.`image/png`) { 2 val result = tile.renderPng().bytes 3 complete(result) 4 }
其實就是調用Tile類的renderPng方法,然后將Png數據轉換成bytes發送到前端。
四、高級瓦片調用
1 val maskedTile = { 2 val poly = maskz.parseGeoJson[Polygon]
3 val extent: Extent = attributeStore.read[TileLayerMetadata[SpatialKey]](LayerId(LayerName, zoom), Fields.metadata).mapTransform(key) 4 tile.mask(extent, poly.geom) 5 }
其中maskz是前端想要顯示內容的區域(Polygon),attributeStore是AccumuloAttributeStore對象,同樣可以看出是一個操作Accumulo的對象,attributeStore主要完成的功能就是讀取當前瓦片的extent即外接矩形范圍。通過調用Tile類的mask方法將請求的polygon與extent做交集,只取相交的部分的數據,再將此數據發到前端,在前端便能看到只顯示設定區域內瓦片的效果。
五、統計分析
1 val layerId = LayerId(layer, 0) 2 val raster = reader.read[SpatialKey, Tile, TileLayerMetadata[SpatialKey]](layerId) 3 val masked = raster.mask(polygon) 4 val mapTransform = masked.metadata.mapTransform 5 val maps = masked map { case (k: SpatialKey, tile: Tile) => 6 val extent: Extent = mapTransform(k) 7 val hist: Histogram[Int] = tile.polygonalHistogram(extent, extent.toPolygon()) 8 9 var max, min = hist.maxValue().getOrElse(0) 10 var count:Long = 0 11 var sum : Double = 0 12 hist.foreach((s1:Int, s2:Long) => {
13 14 if (max < s1) max = s1 15 if (min > s1) min = s1 16 sum += s1 * s2 17 count += s2 18 }) 19 (max, min, sum, count) 20 } 21 val (max, min, sum, count) = maps reduce { case ((z1, a1, s1, c1), (z2, a2, s2, c2)) => (Math.max(z1, z2), Math.min(a1, a2), s1 + s2, c1 + c2) } 22 val avg = sum / count
val layerId = LayerId(layer, 0)表示取的是導入數據的第0層,由於使用floating方式此處必須是0。reader是一個AccumuloLayerReader對象,此處與上面的AccumuloVlaueReader不同之處在於上文中取固定key值得瓦片,此處需要根據范圍進行選擇,masked就是根據polygon篩選出的結果,是一個RDD[(SpatialKey, Tile)]對象,即存儲着范圍內的所有瓦片以及其編號信息。對masked進行map操作,獲取其單個瓦片的extent,以及polygon內的統計信息,算出最大值,最小值以及高程加權和。最后對結果進行reduce操作,獲取整體的最大值、最小值、平均值。(此處平均值算法可能不妥,希望有更好建議的能夠留言,感激!)。將計算到的結果發到前端,前端就能實時顯示統計分析結果。
六、結尾
geotrellis的功能非常強大,此處只是冰山一腳,后續還會進行相關研究,經驗心得會及時總結到這里,以使自己理解的更加透徹,如果能幫助到其他人也是極好的!
七、參考鏈接
一、geotrellis使用初探
二、geotrellis使用(二)geotrellis-chatta-demo以及geotrellis框架數據讀取方式初探
三、geotrellis使用(三)geotrellis數據處理過程分析