在Java中基于Geotools對PostGIS數(shù)據(jù)庫的空間查詢實踐教程
前言
在當今數(shù)字化浪潮下,空間數(shù)據(jù)的應用價值日益凸顯,從城市規(guī)劃到環(huán)境監(jiān)測,從物流配送到地理信息系統(tǒng)(GIS)開發(fā),精準、高效的空間數(shù)據(jù)查詢成為關鍵環(huán)節(jié)。而 Java 作為廣泛應用的編程語言,在與地理空間技術的融合中展現(xiàn)出獨特魅力。Geotools 作為開源的 Java GIS 庫,為 Java 開發(fā)者提供了強大的地理空間數(shù)據(jù)處理能力,猶如一把開啟空間數(shù)據(jù)寶藏之門的鑰匙。PostGIS 則是 PostgreSQL 數(shù)據(jù)庫的空間擴展,能夠存儲和處理復雜的空間數(shù)據(jù)類型,是空間數(shù)據(jù)存儲與管理的得力助手。將 Geotools 與 PostGIS 結(jié)合,意味著我們可以利用 Java 強大的編程生態(tài)和 Geotools 豐富的 GIS 功能,深度挖掘 PostGIS 中海量空間數(shù)據(jù)的潛力。
比如我們需要對某一個風景區(qū)的商業(yè)開發(fā)程度進行評價,需要做的第一件事就是根據(jù)風景區(qū)的范圍也就是AOI數(shù)據(jù),查詢這個AOI數(shù)據(jù)的范圍內(nèi)所有的POI數(shù)據(jù),然后根據(jù)不同的POI類型進行分類,從而計算不同的POI類型在景區(qū)內(nèi)的分布情況,加上一些空間分析和相關計算,從而可以對當前景區(qū)的一個商業(yè)情況進行初步的評估,從而為景區(qū)的優(yōu)質(zhì)發(fā)展提供指導和參考,以下圖為例:
本博客將深入探討這一實踐,從連接配置到復雜空間查詢操作,包括點查詢、區(qū)域范圍查詢以及空間關系判斷等,全方位展示如何在 Java 環(huán)境下借助 Geotools 駕馭 PostGIS 數(shù)據(jù)庫,實現(xiàn)高效精準的空間數(shù)據(jù)檢索,為相關領域開發(fā)者提供實用的技術路徑,助力空間數(shù)據(jù)應用的創(chuàng)新拓展。
一、相關技術背景介紹
這里以長沙岳麓山風景區(qū)為例,主要介紹如何得出岳麓山及風景區(qū)內(nèi)的POI數(shù)據(jù)的分布情況。從而為下一步對該風景區(qū)的商業(yè)及人文指數(shù)進行評價。由此我們需要對岳麓山風景區(qū)的空間范圍數(shù)據(jù),以及有了AOI范圍后對其范圍內(nèi)的POI數(shù)據(jù)進行檢索的流程進行簡單說明。
1、評價對象AOI
關于如何獲取AOI數(shù)據(jù),在之前的博文中我們曾經(jīng)進行過相應的說明。大家可以從相應的官方渠道獲取。也可以從互聯(lián)網(wǎng)圖源來獲取,比如從百度地圖或者高德地圖中也可以獲取數(shù)據(jù)。以高德地圖為例,在下面的查詢界面中可以查看到具體的數(shù)據(jù):
我們可以在網(wǎng)絡請求的地方對這個空間面數(shù)據(jù)進行調(diào)試,也可以將這個數(shù)據(jù)復制到我們的開發(fā)環(huán)境中,從而可以實現(xiàn)離線的空間計算。如下圖所示:
上面的數(shù)據(jù)也是本博客的目標AOI范圍,我們后續(xù)的所有工作都將圍繞這個區(qū)域來展開。 因此,如果對AOI數(shù)據(jù)不是很了解,建議先對相關知識又一個大體的認識以便于更好的掌握相關知識。
2、數(shù)據(jù)處理流程
為了讓大家對整個數(shù)據(jù)處理的流程有一個簡單的認識,這里將整個數(shù)據(jù)的處理流程給讀者進行分享,大致的流程步驟如下:
從整體來說,分為三個階段。第一個階段是AOI即空間查詢面的構建,第二界面是基于AOI面的POI檢索,第三階段就是將兩個圖層進行數(shù)據(jù)疊加后渲染出圖。 下面將結(jié)合流程圖對重要的處理節(jié)點的邏輯進行詳細的介紹。
二、對AOI空間范圍查詢實踐
本節(jié)將重點介紹如何對AOI數(shù)據(jù)進行空間范圍查詢的實現(xiàn)進行說明。對應前文提到的三個階段來分別展開,從查詢目標的空間查詢構建到空間樣式創(chuàng)建,最后對整體成果進行出圖逐級展開。
1、空間查詢構建
空間查詢的構建比較簡單,主要包含三個環(huán)節(jié)的工作,第一個是將字符串類型的AOI數(shù)據(jù)進行解析,剛開始是從高德地圖中獲取AOI字符串,由于是高德地圖的坐標,因此我們首先要對坐標進行轉(zhuǎn)換,將其轉(zhuǎn)換為我們熟悉的WGS84的地理坐標,最后再將轉(zhuǎn)換好的坐標來構建一個完整的查詢面,關鍵代碼如下所示:
/** * - 將AOI字符串轉(zhuǎn)換成Polygon對象 * @return */ public static Polygon convertAoi2Polygon(String aoistr) { String [] AOI_Str_Array = aoistr.split(";"); // 獲取GeometryFactory實例 GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); Coordinate[] coords = {}; if( AOI_Str_Array.length > 0) { coords = new Coordinate[AOI_Str_Array.length]; } //處理坐標 for (int i = 0; i < AOI_Str_Array.length; i++) { String loc = AOI_Str_Array[i]; String [] latlon = loc.split(","); double lng = Double.parseDouble(latlon[0]); double lat = Double.parseDouble(latlon[1]); //將高德坐標轉(zhuǎn)換成WGS84坐標 double [] gcj284 = CoordinateTransformUtil.gcj02towgs84(lng, lat); //System.out.println("高德坐標轉(zhuǎn)wgs84坐標" + gcj284[0] + "=" + gcj284[1]); coords[i] = new Coordinate(gcj284[0], gcj284[1]); } LinearRing shell = geometryFactory.createLinearRing(coords); Polygon polygon = geometryFactory.createPolygon(shell, null); polygon.setSRID(4326);//設置4326的坐標 return polygon; }
經(jīng)過前面的步驟,我們已經(jīng)成功的合成了我們的查詢空間目標,接下來就需要基于Geotools來構建對PostGIS數(shù)據(jù)庫的空間查詢API, 為了演示我們本地的POI功能,這里我們使用本地的POI信息表,當然這張表的數(shù)據(jù)可能與實際情況有一定的出入,僅做參考。關鍵代碼如下:
// 2. 創(chuàng)建PostGIS數(shù)據(jù)存儲 DataStore dataStore = createPostgisDataStore(); // 3. 二次查詢:用該多邊形查詢點數(shù)據(jù)(如查詢橘子洲景區(qū)內(nèi)的POI) String poiLayerName = "biz_poi_info"; FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(); PropertyName geomProperty = ff.property("geom"); // 點數(shù)據(jù)的幾何列名 // 4、空間關系:點在多邊形內(nèi)(WITHIN)或相交(INTERSECTS) Filter spatialFilter = ff.within(geomProperty, ff.literal(aoiPolygon)); Query pointQuery = new Query(poiLayerName, spatialFilter); FeatureSource pointSource = dataStore.getFeatureSource(poiLayerName);
為了方便查看是否成功的執(zhí)行了查詢,這里我們將查詢結(jié)果進行打印輸出。可以在控制臺看到很多的信息輸出,如下圖所示 :
System.out.println("POI數(shù)量:"+points.size()); // 6. 處理結(jié)果 try (FeatureIterator iterator = points.features()) { while (iterator.hasNext()) { Feature pointFeature = iterator.next(); Geometry point = (Geometry) pointFeature.getDefaultGeometryProperty().getValue(); System.out.println("POI坐標: " + point.getCoordinate()); printSimpleFeatureAttributes((SimpleFeature)pointFeature); // 打印屬性 System.out.println("------------------------------------------------------"); } }
運行后在IDE的控制臺中可以看到以下輸出:
能看到以上結(jié)果說明我們的空間查詢函數(shù)構建正確,可以正常執(zhí)行。
2、空間樣式創(chuàng)建
為了能讓展示的效果更好,因此我們需要對獲取的AOI數(shù)據(jù)面和AOI數(shù)據(jù)面內(nèi)的POI數(shù)據(jù)進行標繪,這里我們需要使用SLD的方式來進行美化,兩個生成空間樣式的方法如下:
public static Style createDashedBorderStyle() { StyleFactory sf = CommonFactoryFinder.getStyleFactory(); FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(); PolygonSymbolizer symbolizer = sf.createPolygonSymbolizer( sf.createStroke(ff.literal(Color.DARK_GRAY), ff.literal(0.8)), sf.createFill(ff.literal(Color.BLUE), ff.literal(0.8)), // 80%透明度 null ); Rule rule = sf.createRule(); rule.symbolizers().add(symbolizer); FeatureTypeStyle fts = sf.createFeatureTypeStyle(); fts.rules().add(rule); Style style = sf.createStyle(); style.featureTypeStyles().add(fts); return style; } private static Style createPoiStyle() { StyleFactory sf = CommonFactoryFinder.getStyleFactory(); FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(); // 創(chuàng)建圓形符號 Mark mark = sf.createMark(); mark.setWellKnownName(ff.literal("circle")); mark.setFill(sf.createFill(ff.literal(Color.RED))); mark.setStroke(sf.createStroke(ff.literal(Color.BLACK), ff.literal(1))); Graphic graphic = sf.createDefaultGraphic(); graphic.graphicalSymbols().clear(); graphic.graphicalSymbols().add(mark); PointSymbolizer pointSym = sf.createPointSymbolizer(graphic, null); // 新增震級標注 Font font = sf.createFont(ff.literal("楷體"),ff.literal("Regular"),ff.literal("normal"),ff.literal(18)); // 創(chuàng)建文本標注 TextSymbolizer textSym = sf.createTextSymbolizer( sf.createFill(ff.literal(Color.WHITE)), new Font[] { font }, null, ff.property("name"), // 標注字段 null, null ); // 標注位置(點右側(cè)偏移) AnchorPoint anchor = sf.createAnchorPoint(ff.literal(-0.05), ff.literal(0.05)); Displacement displacement = sf.createDisplacement(ff.literal(0.1), ff.literal(0)); Fill textFill = sf.createFill(ff.literal(Color.RED)); Halo halo = sf.createHalo( sf.createFill(ff.literal(Color.WHITE)), ff.literal(1) ); textSym.setFont(font); textSym.setFill(textFill); textSym.setHalo(halo); //新的設置方法 PointPlacement placement = sf.createPointPlacement(anchor, displacement, ff.literal(0)); textSym.setLabelPlacement(placement); Rule rule = sf.createRule(); rule.symbolizers().add(pointSym); rule.symbolizers().add(textSym); FeatureTypeStyle fts = sf.createFeatureTypeStyle(); fts.rules().add(rule); Style style = sf.createStyle(); style.featureTypeStyles().add(fts); return style; }
創(chuàng)建了以上的樣式之后,我們就可以將樣式和數(shù)據(jù)進行融合,這樣就能繪制出漂亮的地圖了。
3、成果出圖
在完成查詢數(shù)據(jù)的轉(zhuǎn)換以及空間查詢的實現(xiàn)等,接下來就是揭曉答案的時候,我們在代碼層面實現(xiàn)了對岳麓山的AOI構建以及其AOI包圍的POI數(shù)據(jù),關鍵代碼如下:
SimpleFeatureCollection poiCollection = (SimpleFeatureCollection) pointSource.getFeatures(pointQuery); // 7. 創(chuàng)建樣式 Style aoiStyle = createDashedBorderStyle(); Style poiStyle = createPoiStyle(); // 8. 創(chuàng)建地圖內(nèi)容 MapContent mapContent = new MapContent(); SimpleFeatureSource aoiSfs = convert(aoiPolygon); mapContent.addLayer(new FeatureLayer(aoiSfs, aoiStyle)); mapContent.addLayer(new FeatureLayer(poiCollection, poiStyle)); // 9. 設置輸出范圍和尺寸 ReferencedEnvelope mapBounds = poiCollection.getBounds(); mapBounds.expandBy(0.2); // 擴展邊界 // 10、輸出圖像大?。ɡ纾簩挾葂高度) int width = 1920; // 可根據(jù)需求調(diào)整 // 計算地理寬高比 double aspectRatio = mapBounds.getWidth() / mapBounds.getHeight(); //根據(jù)比例計算新高度 int height = (int) Math.round(width / aspectRatio); // 渲染圖片 BufferedImage image = renderMap(mapContent, aoiSfs.getBounds(), width, height); // 保存圖片 ImageIO.write(image, "png", new File("D:/AOI及其包含POI數(shù)據(jù)示意圖.png")); System.out.println("finished"); dataStore.dispose();
使用main函數(shù)或者測試用例都可以執(zhí)行以上的代碼,程序運行后可以在對應的磁盤目錄下看到以下的成果:
從上圖中可以明顯看到,在岳麓上上,一些POI的分布基本還是比較集中的,比如東北方向, 中間的區(qū)域也是非常多。當然,把POI數(shù)據(jù)展現(xiàn)在地圖上還只是一個階段,要想實現(xiàn)商業(yè)化,評估。在有了POI數(shù)據(jù)之后,還要結(jié)合數(shù)據(jù)量,聚類等方面進行空間的分析,且聽我們下回分解。
三、總結(jié)
以上就是本文的主要內(nèi)容,本博客將深入探討基于Geotools對PostGIS數(shù)據(jù)庫的空間查詢實踐,從連接配置到復雜空間查詢操作,包括點查詢、區(qū)域范圍查詢以及空間關系判斷等,全方位展示如何在 Java 環(huán)境下借助 Geotools 駕馭 PostGIS 數(shù)據(jù)庫,實現(xiàn)高效精準的空間數(shù)據(jù)檢索,為相關領域開發(fā)者提供實用的技術路徑,助力空間數(shù)據(jù)應用的創(chuàng)新拓展。如果您也需要對一個AOI數(shù)據(jù)進行范圍內(nèi)的POI進行分析查詢,并且使用的GeoTools的基礎PostGIS訪問功能,那么您可以使用以上實現(xiàn)過程和代碼進行調(diào)試。行文倉促,定有不足之處,歡迎各位朋友在評論區(qū)批評指正,不勝感激。
到此這篇關于在Java中基于Geotools對PostGIS數(shù)據(jù)庫的空間查詢實踐的文章就介紹到這了,更多相關java Geotools PostGIS空間查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot實現(xiàn)前后端分離國際化的示例詳解
Springboot國際化可以幫助使用者在不同語言環(huán)境中構建應用程序,這樣應用程序可以有效地適應不同語言文化背景下的用戶需求。本文主要介紹了SpringBoot實現(xiàn)前后端分離國際化的方法,需要的可以參考一下2023-02-02Java常見報錯類型及解決方案詳細解析(從異常處理到錯誤排查)
這篇文章主要介紹了Java常見報錯類型及解決方案的相關資料,文中結(jié)合具體案例提供針對性解決方案,幫助開發(fā)者快速定位并修復問題,需要的朋友可以參考下2025-05-05Java獲取調(diào)用當前方法的類名或方法名(棧堆信息)的四種方式舉例
在Java編程中我們經(jīng)常需要在運行時獲取當前執(zhí)行的方法名稱,這在日志記錄、性能監(jiān)控、調(diào)試等方面非常有用,這篇文章主要給大家介紹了關于Java獲取調(diào)用當前方法的類名或方法名(棧堆信息)的四種方式,需要的朋友可以參考下2024-09-09SpringCloud服務之間Feign調(diào)用不會帶上請求頭header的解決方法
在Spring?Cloud中,使用Feign進行服務之間的調(diào)用時,默認情況下是不會傳遞header的,這篇文章給大家介紹SpringCloud服務之間Feign調(diào)用不會帶上請求頭header的解決方法,感興趣的朋友一起看看吧2024-01-01