GeoTools 結合 OpenLayers 實現屬性查詢

前言

在GIS開發中,屬性查詢是非常普遍的操作,這是每一個GISer都要掌握的必備技能。實現高效的數據查詢功能可以提升用戶體驗,完成數據的快速可視化表達。

本篇教程在之前一系列文章的基礎上講解如何將使用GeoTools工具結合OpenLayers實現PostGIS空間數據庫數據的屬性查詢功能。

開發環境

本文使用如下開發環境,以供參考。
:::block-1
時間:2025年

GeoTools:v34-SNAPSHOT

IDE:IDEA2025.1.2

JDK:v17

OpenLayers:v9.2.4

Layui:v2.9.14
:::

1. 搭建SpringBoot后端服務

本文接著OpenLayers 從后端服務加載 GeoJSON 數據進行講解,如果還沒有讀過,請從那里開始。

:::block-1
在開始本文之前,請確保你已經安裝好了PostgreSQL數據庫,添加了PostGIS插件,并且已經啟用空間數據拓展。安裝完成之后,你還需要將Shapefile導入空間數據庫。如果你還不了解如何導入空間數據,可參考之前的文章。
:::

1.1. 安裝依賴

pom.xml文件中添加開發所需依賴,其中jdbcpostgresql依賴用于連接數據庫。

<dependencies><dependency><groupId>org.geotools</groupId><artifactId>gt-main</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-geojson</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools.jdbc</groupId><artifactId>gt-jdbc-postgis</artifactId><version>${geotools.version}</version></dependency><!-- PostgreSQL 驅動 --><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><version>42.7.3</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-epsg-hsql</artifactId><version>${geotools.version}</version></dependency>
</dependencies>
<repositories><repository><id>osgeo</id><name>OSGeo Release Repository</name><url>https://repo.osgeo.org/repository/release/</url><snapshots><enabled>false</enabled></snapshots><releases><enabled>true</enabled></releases></repository><repository><id>osgeo-snapshot</id><name>OSGeo Snapshot Repository</name><url>https://repo.osgeo.org/repository/snapshot/</url><snapshots><enabled>true</enabled></snapshots><releases><enabled>false</enabled></releases></repository>
</repositories>

1.2. 創建Countries實體

如下圖是我測試導入的世界國家行政區數據。

可選取部分字段創建業務對象。

package com.example.geotoolsboot.dao;import lombok.Getter;
import lombok.Setter;public class Countries {@Setter@Getterpublic Integer gid; // 要素id@Setter@Getterpublic String sovereignt; // 國家名稱@Setter@Getterpublic String sov_a3; // 國家名稱縮寫@Setter@Getterpublic String admin; // 國家名稱@Setter@Getterpublic String adm0_a3; // 國家名稱縮寫@Setter@Getterpublic String geom; // 幾何字段名稱
}

1.3. 創建數據庫連接

在項目中創建數據庫連接工具類PgUtils,在Map參數中填寫數據庫連接信息。

package com.example.geotoolsboot.utils;import org.geotools.data.postgis.PostgisNGDataStoreFactory;import java.util.HashMap;
import java.util.Map;/*** PostGIS 空間數據庫工具類*/
public class PgUtils {public static Map<String, Object> connectPostGIS(){// 連接PostGIS數據庫Map<String, Object> pgParams = new HashMap();pgParams.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");pgParams.put(PostgisNGDataStoreFactory.HOST.key, "localhost");pgParams.put(PostgisNGDataStoreFactory.PORT.key, "5432");pgParams.put(PostgisNGDataStoreFactory.DATABASE.key, "geodata");pgParams.put(PostgisNGDataStoreFactory.USER.key, "postgres");pgParams.put(PostgisNGDataStoreFactory.PASSWD.key, "123456");pgParams.put(PostgisNGDataStoreFactory.SCHEMA.key, "public"); // 明確指定schemapgParams.put(PostgisNGDataStoreFactory.EXPOSE_PK.key, true);  // 暴露主鍵return pgParams;}
}

1.4. 創建屬性查詢實現類

在項目中創建PgService類用于實現空間數據的屬性過濾操作。定義一個方法attributeFilter,該方法接收一個字符串參數,也就是屬性過濾條件,如"admin = 'China'",最后將查詢結果總數和查詢數據返回。

使用CQL.toFilter方法進行屬性過濾。

// 讀取 PostGIS 空間數據庫數據,并實現屬性過濾
public Map<String,Object> attributeFilter(String filterParams) throws Exception{// 存儲返回對象Map<String,Object> result = new HashMap<>();// 國家數據列表List<Countries> countries = new ArrayList<>();// 創建數據庫連接Map<String, Object> pgParams = PgUtils.connectPostGIS();DataStore dataStore = DataStoreFinder.getDataStore(pgParams);// 數據庫表名String typeName = "countries";SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);// 創建數據過濾器Filter filter = CQL.toFilter(filterParams);// Filter filter = CQL.toFilter("admin = 'China'");SimpleFeatureCollection collection = featureSource.getFeatures(filter);// 統計查詢總數int count = collection.size();result.put("count",count);try(FeatureIterator<SimpleFeature> features = collection.features()) {while (features.hasNext()) {SimpleFeature feature = features.next();// 構造返回數據Countries country = new Countries();country.setGid((Integer) feature.getAttribute("gid"));country.setAdmin((String) feature.getAttribute("admin"));country.setSovereignt((String) feature.getAttribute("sovereignt"));country.setSov_a3((String) feature.getAttribute("sov_a3"));country.setAdm0_a3((String) feature.getAttribute("adm0_a3"));Object geometry = feature.getAttribute("geom");GeometryJSON geometryJSON = new GeometryJSON();StringWriter writer = new StringWriter();geometryJSON.write((Geometry) geometry,writer);String geoJSON = writer.toString();country.setGeom(geoJSON);countries.add(country);}}catch (Exception e){e.printStackTrace();}result.put("countries",countries);return result;
}

1.5. 創建屬性過濾控制層

在測試中,使用注解@CrossOrigin(origins = "*")實現接口允許跨域,注解@GetMapping添加請求訪問路徑。

package com.example.geotoolsboot.controller;
import com.example.geotoolsboot.service.impl.PgService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;/*** 屬性查詢過濾器*/@CrossOrigin(origins = "*") // 允許跨域
@RestController
public class AttributeQueryController {@Autowiredprivate PgService pgService;@GetMapping("/countryList")public Map<String,Object> getCountriesByAttribute(@RequestParam(required = false)  String filterParams) throws Exception{return pgService.attributeFilter(filterParams);}
}

2. 使用 OpenLayers 加載數據

具體使用情況請參考之前的文章:OpenLayers 加載GeoJSON的五種方式

本文前端使用OpenLayers結合Layui框架實現。主要借助Layui表單創建屬性查詢結構,包括屬性字段、查詢條件以及查詢內容數據。

<div class="query-wrap"><form class="layui-form layui-form-pane" action=""><div class="layui-form-item"><label class="layui-form-label">查詢字段</label><div class="layui-input-block"><select name="field" lay-filter="aihao"><option value=""></option><option value="gid" selected>gid(要素編號)</option><option value="admin">admin(國家名稱)</option><option value="adm0_a3">adm0_a3(國家名稱縮寫)</option></select></div></div><div class="layui-form-item"><label class="layui-form-label">查詢條件</label><div class="layui-input-block"><select name="condition" lay-filter="aihao"><option value=""></option><option value="<">&lt</option><option value=">" selected>&gt</option><option value="=">=</option></select></div></div><div class="layui-form-item"><label class="layui-form-label">查詢內容</label><div class="layui-input-block"><input type="text" name="content" lay-verify="required" placeholder="請輸入查詢內容" autocomplete="off"class="layui-input"></div></div><div class="layui-form-item"><label for="">一共查詢到:</label><span class="resultCount">0</span><span>條數據</span></div><div class="layui-form-item"><button class="layui-btn" lay-submit lay-filter="attrQuery">確認</button><button type="reset" class="layui-btn layui-btn-primary">重置</button></div></form>
</div>

CSS 結構樣式:

.query-wrap {position: absolute;padding: 10px;top: 80px;left: 90px;background: #ffffff;width: 250px;border-radius: 2.5px;
}

對于JS部分,在前端直接使用fetchAPI請求接口數據。在每次點擊請求按鈕后都需要調用工具類方法removeLayerByName清除原圖層數據。使用const { field, condition, content } = data.field解構查詢表單數據。后面的代碼內容都是之前寫過的,也比較簡單,就不令行講解了。

layui.use(['form'], function () {const form = layui.form;const layer = layui.layer;// 提交事件form.on('submit(attrQuery)', function (data) {removeLayerByName("country", map)removeLayerByName("highlightLayer", map)// 獲取表單字段值const { field, condition, content } = data.field// 查詢參數const querfilterParams = `${field + " " + condition + " '" + content + "'"}`// 后端服務地址const JSON_URL = "http://127.0.0.1:8080/countryList?filterParams=" + querfilterParamsfetch(JSON_URL).then(response => response.json().then(result => {// 獲取查詢數量const resultCount = result.countdocument.querySelector(".resultCount").textContent = resultCount// 獲取查詢結果const countries = result.countriesconst features = countries.map(item => {const feat = {}feat.type = "Feature"feat.geometry = JSON.parse(item.geom)feat.properties = itemfeat.properties.color = `hsl(${Math.floor(Math.random() * 360)}, 100%, 50%)`const feature = new ol.format.GeoJSON().readFeature(feat)return feature})const vectorSource = new ol.source.Vector({features: features,format: new ol.format.GeoJSON()})// 行政區矢量圖層const regionLayer = new ol.layer.Vector({source: vectorSource,style: {"text-value": ["string", ['get', 'admin']],'fill-color': ['string', ['get', 'color'], '#eee'],}})regionLayer.set("layerName", "country")map.addLayer(regionLayer)map.getView().setCenter([108.76623301275802, 34.22939602197002])map.getView().setZoom(4.5)// 高亮圖層const highlightLayer = new ol.layer.Vector({source: new ol.source.Vector({}),map: map,style: {"stroke-color": '#3CF9FF',"stroke-width": 2.5}})// Popup 模板const popupColums = [{name: "gid",comment: "要素編號"},{name: "admin",comment: "國家名稱"},{name: "adm0_a3",comment: "簡稱"},{name: "color",comment: "顏色"}]// 高亮要素let highlightFeat = undefinedfunction showPopupInfo(pixel) {regionLayer.getFeatures(pixel).then(features => {// 若未查詢到要素,則退出if (!features.length) {if (highlightFeat) {highlightLayer.getSource().removeFeature(highlightFeat)highlightFeat = undefined}return}// 獲取要素屬性const properties = features[0].getProperties()// 將事件坐標轉換為地圖坐標const coords = map.getCoordinateFromPixel(pixel)if (features[0] != highlightFeat) {// 移除高亮要素if (highlightFeat) {highlightLayer.getSource().removeFeature(highlightFeat)highlightFeat = undefined}highlightLayer.getSource().addFeature(features[0])highlightFeat = features[0]}openPopupTable(properties, popupColums, coords)})}// 監聽地圖鼠標移動事件map.on("pointermove", evt => {// 若正在拖拽地圖,則退出if (evt.dragging) returnconst pixel = map.getEventPixel(evt.originalEvent)showPopupInfo(pixel)})// 監聽地圖鼠標點擊事件map.on("click", evt => {console.log(evt.coordinate)// 若正在拖拽地圖,則退出if (evt.dragging) returnshowPopupInfo(evt.pixel)})}))return false; // 阻止默認 form 跳轉});});

OpenLayers示例數據下載,請回復關鍵字:ol數據

全國信息化工程師-GIS 應用水平考試資料,請回復關鍵字:GIS考試

【GIS之路】 已經接入了智能助手,歡迎關注,歡迎提問。

歡迎訪問我的博客網站-長談GIShttp://shanhaitalk.com

都看到這了,不要忘記點贊、收藏 + 關注

本號不定時更新有關 GIS開發 相關內容,歡迎關注 !

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86701.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86701.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86701.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue-27(實踐練習:將現有組件重構為使用組合式 API)

實踐練習:將現有組件重構為使用組合式 API 理解重構過程 重構是任何開發者的關鍵技能,尤其是在采用新范式如 Vue.js 中的 Composition API 時。它涉及在不改變外部行為的情況下重新組織現有代碼,旨在提高可讀性、可維護性和可重用性。在從 Options API 遷移到 Composition…

基于Uniapp+SpringBoot+Vue 的在線商城小程序

開發系統:Windows10 架構模式:MVC/前后端分離 JDK版本: Java JDK1.8 開發工具:IDEA 數據庫版本: mysql8.0 數據庫可視化工具: navicat 服務器: SpringBoot自帶 apache tomcat 主要技術: Java,Springboot,mybatis,mysql,jquery,html,vue 角色:用戶 商家 管理員 用戶菜單:首頁:商…

華為云Flexus+DeepSeek征文|利用華為云一鍵部署的Dify平臺構建高效智能電商客服系統實戰

目錄 前言 1 華為云快速搭建 Dify-LLM 應用平臺 1.1 一鍵部署簡介 1.2 設置管理員賬號登錄dify平臺 2 接入 DeepSeek 大模型與 Reranker 模型 2.1 接入自定義 LLM 模型 2.2 設置 Reranker 模型 3 構建電商知識庫 3.1 數據源選擇 3.2 分段設置與清洗 3.3 處理并完成 …

python應用day07---pyechars模塊詳解

1.pyecharts安裝: pip install pyecharts 2.pyecharts入門: # 1.導入模塊 from pyecharts.charts import Line# 2.創建Line對象 line Line() # 添加數據 line.add_xaxis(["中國", "美國", "印度"]) line.add_yaxis("GDP數據", [30…

高檔背景色

https://andi.cn/page/622250.html

教學視頻畫中畫播放(PICTURE-IN-PICTURE)效果

視頻平臺的畫中畫&#xff08;PIP&#xff09;功能通過小窗播放提升用戶體驗&#xff1a;1&#xff09;支持多任務處理&#xff0c;如邊看教程邊操作文檔&#xff1b;2&#xff09;減少應用跳出率&#xff0c;增強用戶粘性&#xff1b;3&#xff09;優化屏幕空間利用&#xff1…

MySQL (一):數據類型,完整性約束和表間關系

在當今數據驅動的時代&#xff0c;數據庫作為數據存儲與管理的核心工具&#xff0c;其重要性不言而喻。MySQL 作為一款廣泛應用的開源數據庫&#xff0c;憑借其高性能、高可靠性和豐富的功能&#xff0c;深受開發者喜愛。本文作為 MySQL 系列博客的開篇&#xff0c;將帶你深入了…

【軟考高項論文】信息系統項目的資源管理

摘要 本文圍繞信息系統項目的資源管理展開論述。首先闡述了項目資源管理的基本過程&#xff0c;包括資源規劃、估算、獲取、配置、監控和釋放等關鍵步驟&#xff0c;并給出資源分解結構示例。接著結合2024年參與管理的某信息系統項目實際情況&#xff0c;詳細說明資源管理的具…

阿里云Ubuntu服務器上安裝MySQL并配置遠程連接

1. 安裝MySQL 首先連接到你的Ubuntu服務器&#xff0c;然后執行&#xff1a; # 更新軟件包列表 sudo apt update# 安裝MySQL服務器 sudo apt install mysql-server# 啟動MySQL服務 sudo systemctl start mysql# 設置MySQL開機自啟 sudo systemctl enable mysql# 檢查MySQL狀態…

STM32HAL 旋轉編碼器教程

配置時鐘編碼模式讀取方法&#xff1a; if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&htim1) 0){count - __HAL_TIM_GET_COUNTER(&htim1);}else{count __HAL_TIM_GET_COUNTER(&htim1);}bsp_dtUInt32_show(count);__HAL_TIM_SET_COUNTER(&htim1, 0); 通過 __HAL_TIM…

激光束修復手機屏任意層不良區域,實現液晶線路激光修復原理

摘要 手機屏結構多層復合&#xff0c;任意層線路不良嚴重影響顯示質量。激光束憑借高能量密度與可調控性&#xff0c;能夠穿透不同介質精準作用于目標層。本文基于激光與多層材料相互作用機制&#xff0c;解析激光束對手機屏各層不良區域的修復原理&#xff0c;為全層液晶線路…

【軟件開發】架構與架構師

文章目錄 一、前言二、關于系統架構1. 保障用戶訪問速度2. 支持大規模并發請求3. 應對復雜業務邏輯4. 應對海量數據的存儲與讀寫三、關于系統架構師四、關于安全架構1. 產品安全架構2. 安全體系架構五、關于安全架構師一、前言 在系統建設與技術架構實踐不斷推進的背景下,關于…

Blender速成班-基礎篇2

視頻教程&#xff1a;【第一章】基礎操作_嗶哩嗶哩_bilibili 目錄 編輯模式 1.1側邊屬性 擠出選區——E 擠出方式選擇——AltE ?編輯 內插面——I 倒角——CtrlB 環切——CtrlR 旋繞 本片繼續基于視頻教程介紹Blender的一些基礎操作 勾選Cavity使物體邊線更清晰 編…

對象進階與擴展

目錄 創建對象 Object.create&#xff08;&#xff09; 原型 原型操作 原型污染 對象屬性 屬性特征 枚舉屬性 Object.keys&#xff08;&#xff09; Object.getOwnPropertyNames&#xff08;&#xff09; Object.getOwnPropertyDescriptor&#xff08;&#xff09; O…

理解圖像的隨機噪聲

圖像灰度信息很難精確測量&#xff0c;一般情況下測量值總在真實值附近晃動&#xff0c;使用概率模型可以對該隨機性建模&#xff0c;大致如下&#xff1a; 1 概率密度函數 1&#xff09;隨機變量 x 的概率密度函數 p(x) 定義為&#xff1a;當 趨近于 0 時&#xff0c;在區間 上…

華為云鏡像倉庫下載 selenium/standalone-chrome 鏡像

你可以使用以下步驟從華為云鏡像倉庫下載 selenium/standalone-chrome 鏡像&#xff1a; 1. 登錄華為云鏡像倉庫&#xff08;如果需要認證&#xff09; bash sudo docker login -u <用戶名> -p <密碼> swr.cn-north-4.myhuaweicloud.com 如果沒有華為云賬號&…

Push-T, AloHa, Rlbench三個仿真環境信息

1.Push-T 很好兄弟&#xff0c;你問得很關鍵&#xff1a;你給我的三段代碼其實是一套完整的推理錄像 pipeline&#xff0c;它們之間既有獨立功能&#xff0c;又有順序依賴關系。我來幫你分段解釋&#xff0c;每段是什么功能、三段之間怎么配合&#xff0c;讓你徹底搞明白。 &a…

Linux信號機制:從入門到精通

嘿&#xff0c;小伙伴們&#xff01;今天我要和大家聊一個Linux系統中非常有趣又重要的話題——信號機制。別擔心&#xff0c;雖然信號聽起來有點高深&#xff0c;但我會用最通俗易懂的語言&#xff0c;配合清晰的圖表&#xff0c;帶你徹底搞懂這個概念&#xff01; 什么是信號…

Vue3項目引入高德地圖【超詳細教程】

前言 在 Vue 3 項目中集成高德地圖&#xff08;AMap&#xff09;是一個常見的需求。本文將詳細介紹如何在 Vue 3 項目中使用高德地圖&#xff0c;包括安裝配置、基本使用以及一些進階功能的實現。 一、環境準備 1.1 vue3項目初始化 步驟 1&#xff1a;初始化項目 npm crea…

blender mcp安裝(完全免費的ai建模)

1.最關鍵的一步&#xff0c;建議最早執行(就是安裝uvx) mac系統執行 brew install uvwindows執行 powershell -c "irm https://astral.sh/uv/install.ps1 | iex" 出現這一步就成功安裝uvx了&#xff0c;因為mcp需要使用uvx 2.第二步驟 github地址: https://gith…