2 geotools入門示例

1. 設置 Spring Boot 項目并集成 GeoTools 依賴

首先,你需要創建一個新的 Spring Boot 項目。你可以使用 Spring Initializr 來快速生成項目骨架。

選擇以下依賴:

  • Web: Spring Web (用于創建 REST API)
  • Developer Tools: Spring Boot DevTools (可選,用于熱加載)

添加 GeoTools 依賴:

在你的 pom.xml (如果你使用 Maven) 或 build.gradle (如果你使用 Gradle) 文件中添加 GeoTools 的依賴。GeoTools 是一個龐大的庫,你可以只添加你需要的模塊。為了讀取 Shapefile 和 GeoJSON,你至少需要以下依賴:

Maven (pom.xml):

<properties><java.version>17</java.version><geotools.version>29.1</geotools.version>  </properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-main</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-shapefile</artifactId><version>${geotools.version}</version></dependency><dependency><groupId>org.geotools</groupId><artifactId>gt-data</artifactId> <version>${geotools.version}</version></dependency><dependency><groupId>org.geotools.xsd</groupId><artifactId>gt-xsd-geojson</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><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope> </dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></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>

注意: 請將 ${geotools.version} 替換為最新的 GeoTools 穩定版本。你可以在 GeoTools 官網 或 Maven 中央倉庫查找最新版本。

2. 讀取常見的空間數據格式

理解核心 GeoTools 概念

在開始讀取數據之前,我們先來理解幾個 GeoTools 的核心概念:

  • DataStore: DataStore 是訪問特定數據格式或服務的入口點。例如,ShapefileDataStore 用于 Shapefile,GeoJSONDataStore 用于 GeoJSON 文件,JDBCDataStore 用于數據庫。
  • DataStoreFactorySpi: 這是用于創建 DataStore 實例的工廠接口。例如,ShapefileDataStoreFactoryGeoJSONDataStoreFactoryJDBCDataStoreFactory (具體到 PostGIS 是 PostGISDialectFactoryJDBCDataStoreFactory 結合使用)。
  • FeatureSource: 一旦你有了 DataStore,你可以通過它獲取 FeatureSourceFeatureSource 代表了一層地理要素 (features),你可以從中讀取要素。它通常是只讀的,如果需要寫入,則使用其子接口 FeatureStore
  • FeatureCollection: FeatureCollection 是從 FeatureSource 中檢索到的要素的集合。
  • FeatureIterator: FeatureIterator 用于遍歷 FeatureCollection 中的每一個要素。重要的是:使用完畢后一定要關閉 FeatureIterator 以釋放資源。
  • SimpleFeature: SimpleFeature 代表一個單獨的地理要素,它包含了地理屬性 (geometry) 和非地理屬性 (attributes)。它的結構由 SimpleFeatureType 定義。
讀取 Shapefile

假設你有一個名為 your_shapefile.shp 的 Shapefile 文件 (通常還伴隨著 .dbf, .shx 等輔助文件)。

創建一個服務類來處理數據讀取,例如 SpatialDataService.java

package com.example.geotoolsdemo.service;import org.geotools.api.data.*;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.springframework.stereotype.Service;import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class SpatialDataService {public List<Map<String, Object>> readShapefile(String filePath) throws IOException {File file = new File(filePath);if (!file.exists()) {throw new IOException("Shapefile not found at: " + filePath);}Map<String, Object> params = new HashMap<>();try {params.put("url", file.toURI().toURL());} catch (MalformedURLException e) {throw new RuntimeException("Failed to convert file path to URL", e);}params.put("create spatial index", true); // 可選,提高性能ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();ShapefileDataStore dataStore = null;List<Map<String, Object>> featuresList = new ArrayList<>();try {dataStore = (ShapefileDataStore) dataStoreFactory.createDataStore(params);if (dataStore == null) {throw new IOException("Could not create ShapefileDataStore for: " + filePath);}String typeName = dataStore.getTypeNames()[0]; // Shapefile 通常只包含一個類型FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(typeName);FeatureCollection<SimpleFeatureType, SimpleFeature> collection = source.getFeatures();try (FeatureIterator<SimpleFeature> features = collection.features()) {while (features.hasNext()) {SimpleFeature feature = features.next();Map<String, Object> featureAttributes = new HashMap<>();feature.getProperties().forEach(property -> {// GeoTools 的 geometry 對象不能直接序列化為 JSON,// 在 REST API 中通常會轉換為 GeoJSON 格式的字符串或 WKTif (property.getValue() instanceof com.locationtech.jts.geom.Geometry) {// 在 API 控制器中處理幾何對象的 GeoJSON 轉換featureAttributes.put(property.getName().getLocalPart(), property.getValue().toString()); // 暫時用 WKT} else {featureAttributes.put(property.getName().getLocalPart(), property.getValue());}});featuresList.add(featureAttributes);}}} finally {if (dataStore != null) {dataStore.dispose(); // 非常重要:釋放資源}}return featuresList;}
}

重要:

  • 確保你的 Shapefile 路徑正確。
  • 使用 try-with-resources 或者在 finally 塊中調用 dataStore.dispose()features.close() 來釋放資源,這非常重要,否則可能導致文件鎖等問題。
讀取 GeoJSON

GeoTools 提供了兩種主要方式來處理 GeoJSON:

  1. 使用 GeoJSONDataStoreFactory: 這種方式與其他 DataStore 類似,更通用。
  2. 直接使用 gt-geojson 模塊 (例如 GeoJSONReaderFeatureJSON): 這種方式更直接,有時更簡單,特別是當你只需要讀取 GeoJSON 內容而不一定需要完整的 DataStore 抽象時。

方法 1: 使用 GeoJSONDataStoreFactory

package com.example.geotoolsdemo.service;// ... 其他 import ...
import org.geotools.data.geojson.GeoJSONDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;// ... 在 SpatialDataService.java 中添加以下方法 ...public List<Map<String, Object>> readGeoJSON(String filePath) throws IOException {File file = new File(filePath);if (!file.exists()) {throw new IOException("GeoJSON file not found at: " + filePath);}Map<String, Object> params = new HashMap<>();try {params.put(GeoJSONDataStoreFactory.URLP.key, file.toURI().toURL());} catch (MalformedURLException e) {throw new RuntimeException("Failed to convert file path to URL", e);}GeoJSONDataStoreFactory dataStoreFactory = new GeoJSONDataStoreFactory();DataStore dataStore = null;List<Map<String, Object>> featuresList = new ArrayList<>();try {dataStore = dataStoreFactory.createDataStore(params);if (dataStore == null) {throw new IOException("Could not create GeoJSONDataStore for: " + filePath);}String typeName = dataStore.getTypeNames()[0];SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);SimpleFeatureCollection collection = featureSource.getFeatures();try (FeatureIterator<SimpleFeature> features = collection.features()) {while (features.hasNext()) {SimpleFeature feature = features.next();Map<String, Object> featureAttributes = new HashMap<>();feature.getProperties().forEach(property -> {if (property.getValue() instanceof com.locationtech.jts.geom.Geometry) {featureAttributes.put(property.getName().getLocalPart(), property.getValue().toString()); // 暫時用 WKT} else {featureAttributes.put(property.getName().getLocalPart(), property.getValue());}});featuresList.add(featureAttributes);}}} finally {if (dataStore != null) {dataStore.dispose();}}return featuresList;
}

方法 2: 直接使用 gt-geojson (例如 FeatureJSON)

這個模塊允許你更直接地將 GeoJSON 字符串或流解析為 FeatureCollection

package com.example.geotoolsdemo.service;// ... 其他 import ...
import org.geotools.geojson.feature.FeatureJSON; // 用于解析和編碼 FeatureCollection
import org.geotools.feature.FeatureCollection;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;import java.io.FileInputStream;
import java.io.InputStream;// ... 在 SpatialDataService.java 中添加以下方法 ...public List<Map<String, Object>> readGeoJSONDirectly(String filePath) throws IOException {File file = new File(filePath);if (!file.exists()) {throw new IOException("GeoJSON file not found at: " + filePath);}List<Map<String, Object>> featuresList = new ArrayList<>();FeatureJSON fjson = new FeatureJSON(); // 用于讀取 FeatureCollectiontry (InputStream in = new FileInputStream(file)) {FeatureCollection<SimpleFeatureType, SimpleFeature> collection = fjson.readFeatureCollection(in);try (FeatureIterator<SimpleFeature> features = collection.features()) {while (features.hasNext()) {SimpleFeature feature = features.next();Map<String, Object> featureAttributes = new HashMap<>();feature.getProperties().forEach(property -> {if (property.getValue() instanceof com.locationtech.jts.geom.Geometry) {featureAttributes.put(property.getName().getLocalPart(), property.getValue().toString()); // 暫時用 WKT} else {featureAttributes.put(property.getName().getLocalPart(), property.getValue());}});featuresList.add(featureAttributes);}}}return featuresList;
}

選擇哪種方式取決于你的具體需求和偏好。DataStore 方式更通用,而直接解析更輕量級。

PostGIS/其他空間數據庫 (初步了解)

使用 JDBCDataStoreFactory 可以連接到多種支持 JDBC 的空間數據庫,包括 PostGIS。

你需要:

  1. PostGIS (或其他空間數據庫) 的 JDBC驅動 (例如 postgresql 驅動)。
  2. 數據庫連接參數 (主機, 端口, 數據庫名, 用戶名, 密碼等)。
package com.example.geotoolsdemo.service;// ... 其他 import ...
import org.geotools.data.DataStoreFinder;
import org.geotools.data.postgis.PostgisNGDataStoreFactory; // 推薦使用NG (Next Generation) 版本// ... 在 SpatialDataService.java 中添加以下方法 ...public List<Map<String, Object>> readPostGIS(String tableName) throws IOException {Map<String, Object> params = new HashMap<>();params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");params.put(PostgisNGDataStoreFactory.HOST.key, "localhost"); // 你的數據庫主機params.put(PostgisNGDataStoreFactory.PORT.key, 5432);        // 你的數據庫端口params.put(PostgisNGDataStoreFactory.DATABASE.key, "your_database"); // 你的數據庫名params.put(PostgisNGDataStoreFactory.SCHEMA.key, "public");    // 你的模式名 (通常是 public)params.put(PostgisNGDataStoreFactory.USER.key, "your_user");      // 你的用戶名params.put(PostgisNGDataStoreFactory.PASSWD.key, "your_password");  // 你的密碼// params.put(PostgisNGDataStoreFactory.SSL_MODE.key, "disable"); // 根據你的 SSL 配置DataStore dataStore = null;List<Map<String, Object>> featuresList = new ArrayList<>();try {// 使用 DataStoreFinder 自動查找合適的工廠dataStore = DataStoreFinder.getDataStore(params);if (dataStore == null) {throw new IOException("Could not connect to PostGIS database. Check connection parameters.");}// 或者直接使用 PostgisNGDataStoreFactory// PostgisNGDataStoreFactory factory = new PostgisNGDataStoreFactory();// if (!factory.canProcess(params)) {//     throw new IOException("PostgisNGDataStoreFactory cannot process the provided parameters.");// }// dataStore = factory.createDataStore(params);SimpleFeatureSource featureSource = dataStore.getFeatureSource(tableName); // tableName 是數據庫中的表名SimpleFeatureCollection collection = featureSource.getFeatures();try (FeatureIterator<SimpleFeature> features = collection.features()) {while (features.hasNext()) {SimpleFeature feature = features.next();Map<String, Object> featureAttributes = new HashMap<>();feature.getProperties().forEach(property -> {if (property.getValue() instanceof com.locationtech.jts.geom.Geometry) {featureAttributes.put(property.getName().getLocalPart(), property.getValue().toString()); // 暫時用 WKT} else {featureAttributes.put(property.getName().getLocalPart(), property.getValue());}});featuresList.add(featureAttributes);}}} finally {if (dataStore != null) {dataStore.dispose();}}return featuresList;
}

注意:

  • 確保已將 PostgreSQL JDBC 驅動添加到項目的依賴中。
  • 替換上述代碼中的數據庫連接參數為你自己的配置。
  • DataStoreFinder.getDataStore(params) 會嘗試根據參數找到合適的 DataStoreFactory。對于 PostGIS,通常會找到 PostgisNGDataStoreFactory

3. 創建一個簡單的 Spring Boot REST API 返回空間數據 (GeoJSON 格式)

現在我們來創建一個 REST 控制器,它將使用 SpatialDataService 讀取數據,并將數據轉換為 GeoJSON 格式返回。

GeoTools 的 gt-geojson 模塊中的 FeatureJSON 類可以非常方便地將 FeatureCollection 或單個 Feature 編碼為 GeoJSON 字符串。

創建 SpatialDataController.java:

package com.example.geotoolsdemo.controller;import com.example.geotoolsdemo.service.SpatialDataService;
import org.geotools.api.data.*;
import org.geotools.api.feature.simple.SimpleFeature;
import org.geotools.api.feature.simple.SimpleFeatureType;
import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geojson.feature.FeatureJSON; // 用于編碼為 GeoJSON
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@RestController
@RequestMapping("/api/spatial")
public class SpatialDataController {@Autowiredprivate SpatialDataService spatialDataService;// 將 FeatureCollection 轉換為 GeoJSON 字符串的輔助方法private String convertFeatureCollectionToGeoJSON(FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection) throws IOException {StringWriter writer = new StringWriter();FeatureJSON featureJSON = new FeatureJSON();featureJSON.writeFeatureCollection(featureCollection, writer);return writer.toString();}@GetMapping(value = "/shapefile", produces = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<String> getShapefileAsGeoJSON(@RequestParam String filePath) {try {File file = new File(filePath);Map<String, Object> params = new HashMap<>();params.put("url", file.toURI().toURL());params.put("create spatial index", false);ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createDataStore(params);if (dataStore == null) {return ResponseEntity.status(500).body("{\"error\":\"Could not create ShapefileDataStore for: " + filePath + "\"}");}String typeName = dataStore.getTypeNames()[0];FeatureSource<SimpleFeatureType, SimpleFeature> source = dataStore.getFeatureSource(typeName);FeatureCollection<SimpleFeatureType, SimpleFeature> collection = source.getFeatures();String geoJson = convertFeatureCollectionToGeoJSON(collection);dataStore.dispose(); // 確保釋放資源return ResponseEntity.ok(geoJson);} catch (MalformedURLException e) {return ResponseEntity.status(500).body("{\"error\":\"Invalid file path URL: " + e.getMessage() + "\"}");} catch (IOException e) {return ResponseEntity.status(500).body("{\"error\":\"Error reading Shapefile: " + e.getMessage() + "\"}");}}@GetMapping(value = "/geojson-file", produces = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<String> getGeoJSONFileAsGeoJSON(@RequestParam String filePath) {// 對于 GeoJSON 文件,我們實際上可以直接返回其內容,// 但為了演示 GeoTools 的處理流程,我們先讀取再寫回。try {File file = new File(filePath);Map<String, Object> params = new HashMap<>();params.put(org.geotools.data.geojson.GeoJSONDataStoreFactory.URLP.key, file.toURI().toURL());org.geotools.data.geojson.GeoJSONDataStoreFactory dataStoreFactory = new org.geotools.data.geojson.GeoJSONDataStoreFactory();DataStore dataStore = dataStoreFactory.createDataStore(params);if (dataStore == null) {return ResponseEntity.status(500).body("{\"error\":\"Could not create GeoJSONDataStore for: " + filePath + "\"}");}String typeName = dataStore.getTypeNames()[0];SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);SimpleFeatureCollection collection = featureSource.getFeatures();String geoJson = convertFeatureCollectionToGeoJSON(collection);dataStore.dispose();return ResponseEntity.ok(geoJson);} catch (MalformedURLException e) {return ResponseEntity.status(500).body("{\"error\":\"Invalid file path URL: " + e.getMessage() + "\"}");} catch (IOException e) {return ResponseEntity.status(500).body("{\"error\":\"Error reading GeoJSON file: " + e.getMessage() + "\"}");}}@GetMapping(value = "/postgis", produces = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<String> getPostgisLayerAsGeoJSON(@RequestParam String host,@RequestParam int port,@RequestParam String database,@RequestParam String schema,@RequestParam String user,@RequestParam String password,@RequestParam String tableName) {Map<String, Object> params = new HashMap<>();params.put(PostgisNGDataStoreFactory.DBTYPE.key, "postgis");params.put(PostgisNGDataStoreFactory.HOST.key, host);params.put(PostgisNGDataStoreFactory.PORT.key, port);params.put(PostgisNGDataStoreFactory.DATABASE.key, database);params.put(PostgisNGDataStoreFactory.SCHEMA.key, schema);params.put(PostgisNGDataStoreFactory.USER.key, user);params.put(PostgisNGDataStoreFactory.PASSWD.key, password);DataStore dataStore = null;try {dataStore = DataStoreFinder.getDataStore(params);if (dataStore == null) {return ResponseEntity.status(500).body("{\"error\":\"Could not connect to PostGIS database.\"}");}SimpleFeatureSource featureSource = dataStore.getFeatureSource(tableName);SimpleFeatureCollection collection = featureSource.getFeatures();String geoJson = convertFeatureCollectionToGeoJSON(collection);return ResponseEntity.ok(geoJson);} catch (IOException e) {return ResponseEntity.status(500).body("{\"error\":\"Error reading from PostGIS: " + e.getMessage() + "\"}");} finally {if (dataStore != null) {dataStore.dispose();}}}// 一個簡單的端點,用于測試直接創建 GeoJSON FeatureCollection@GetMapping(value = "/sample-geojson", produces = MediaType.APPLICATION_JSON_VALUE)public ResponseEntity<String> getSampleGeoJSON() throws IOException {// 1. 定義 FeatureType (Schema)SimpleFeatureType featureType = DataUtilities.createType("Location","geometry:Point:srid=4326," + // a geometry attribute: Point type, SRID 4326 (WGS84)"name:String," + // a String attribute"population:Integer" // an Integer attribute);// 2. 創建 FeatureCollectionList<SimpleFeature> features = new ArrayList<>();GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory();SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);// 創建第一個 FeaturePoint point1 = geometryFactory.createPoint(new Coordinate(-73.985130, 40.758896)); // Times SquarefeatureBuilder.add(point1);featureBuilder.add("Times Square");featureBuilder.add(10000); // 假設的人口SimpleFeature feature1 = featureBuilder.buildFeature("fid-1");features.add(feature1);// 創建第二個 FeaturePoint point2 = geometryFactory.createPoint(new Coordinate(-74.0060, 40.7128)); // Wall StreetfeatureBuilder.add(point2);featureBuilder.add("Wall Street");featureBuilder.add(5000);SimpleFeature feature2 = featureBuilder.buildFeature("fid-2");features.add(feature2);SimpleFeatureCollection collection = new ListFeatureCollection(featureType, features);// 3. 將 FeatureCollection 轉換為 GeoJSON 字符串String geoJson = convertFeatureCollectionToGeoJSON(collection);return ResponseEntity.ok(geoJson);}
}

解釋:

  • @RestController@RequestMapping("/api/spatial") 定義了 API 的基礎路徑。
  • @GetMapping 定義了處理 GET 請求的端點。
  • produces = MediaType.APPLICATION_JSON_VALUE 表明端點將返回 JSON 格式的數據。
  • @RequestParam String filePath 允許你通過 URL 參數傳遞文件路徑 (例如 http://localhost:8080/api/spatial/shapefile?filePath=/path/to/your/data.shp)。在生產環境中,直接暴露文件路徑是非常不安全的,這里僅作演示。你應該使用更安全的方式來管理和訪問數據文件。
  • 我們重用了之前 SpatialDataService 中讀取數據的邏輯 (或者直接在控制器中實現)。
  • FeatureJSON().writeFeatureCollection(collection, writer)FeatureCollection 轉換為 GeoJSON 字符串。
  • 錯誤處理和資源釋放:在控制器中,我們同樣需要確保 DataStore 等資源被正確關閉。

運行你的 Spring Boot 應用:

在你的項目根目錄下運行:

./mvnw spring-boot:run
# 或者 (如果你使用的是 Gradle)
./gradlew bootRun

然后你可以通過瀏覽器或 Postman/curl 等工具訪問你的 API 端點:

  • http://localhost:8080/api/spatial/shapefile?filePath=你的shapefile絕對路徑.shp
  • http://localhost:8080/api/spatial/geojson-file?filePath=你的geojson絕對路徑.geojson
  • http://localhost:8080/api/spatial/postgis?host=...&port=...&database=...&schema=...&user=...&password=...&tableName=...
  • http://localhost:8080/api/spatial/sample-geojson (用于測試)

4. (可選) 在前端簡單展示 (例如,使用 Leaflet.js 或 OpenLayers)

這一步是可選的,但有助于你理解數據如何在前端地圖上顯示。我們將使用 Leaflet.js,因為它相對簡單易用。

  1. 創建一個簡單的 HTML 文件 (例如 src/main/resources/static/index.html):

    Spring Boot 會自動從 src/main/resources/static 或 src/main/resources/public 目錄下提供靜態內容。

    代碼段

    <!DOCTYPE html>
    <html>
    <head><title>GeoTools & Spring Boot - Leaflet Map</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="crossorigin=""></script><style>#map { height: 600px; }</style>
    </head>
    <body><div id="map"></div>
    <button onclick="loadShapefileData()">Load Shapefile Data (Sample)</button>
    <button onclick="loadGeoJSONFileData()">Load GeoJSON File Data (Sample)</button>
    <button onclick="loadSampleData()">Load Sample GeoJSON</button><script>var map = L.map('map').setView([40.7128, -74.0060], 10); // 默認視圖 (紐約)L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'}).addTo(map);var geoJsonLayer; // 用于存儲 GeoJSON 圖層,方便移除和更新function displayGeoJSON(geoJsonData) {if (geoJsonLayer) {map.removeLayer(geoJsonLayer); // 移除舊圖層}geoJsonLayer = L.geoJSON(geoJsonData, {onEachFeature: function (feature, layer) {// 嘗試顯示一個屬性作為彈出窗口if (feature.properties) {let popupContent = '';for (const key in feature.properties) {popupContent += `<strong>${key}:</strong> ${feature.properties[key]}<br>`;}if (popupContent === '') {popupContent = "No properties found for this feature.";}layer.bindPopup(popupContent);}}}).addTo(map);// 縮放到圖層范圍if (geoJsonLayer.getBounds().isValid()) {map.fitBounds(geoJsonLayer.getBounds());}}function loadShapefileData() {// !!!重要!!!// 在生產環境中,不要直接將本地文件路徑暴露給前端。// 這里假設你有一個本地的 Shapefile,你需要替換為你的實際路徑。// 為了安全和方便,更好的做法是讓后端API知道數據源的位置,// 前端只需要調用一個不帶敏感路徑參數的API端點。// 例如: /api/spatial/data/myPredefinedShapefile// 這里為了簡單演示,我們仍然使用 filePath 參數。// 請將 '/path/to/your/data.shp' 替換為你的 Shapefile 文件的 *URL編碼后的絕對路徑*// 或者,將后端API修改為不需要此參數,而是從配置中讀取路徑。const filePath = prompt("Enter the absolute path to your .shp file (e.g., C:/data/my_shapefile.shp or /Users/user/data/my_shapefile.shp):");if (!filePath) return;fetch(`/api/spatial/shapefile?filePath=${encodeURIComponent(filePath)}`).then(response => {if (!response.ok) {return response.json().then(err => { throw new Error(err.error || `HTTP error! status: ${response.status}`) });}return response.json();}).then(data => {console.log("Shapefile data loaded:", data);displayGeoJSON(data);}).catch(error => console.error('Error loading Shapefile data:', error));}function loadGeoJSONFileData() {const filePath = prompt("Enter the absolute path to your .geojson file (e.g., C:/data/my_data.geojson or /Users/user/data/my_data.geojson):");if (!filePath) return;fetch(`/api/spatial/geojson-file?filePath=${encodeURIComponent(filePath)}`).then(response => {if (!response.ok) {return response.json().then(err => { throw new Error(err.error || `HTTP error! status: ${response.status}`) });}return response.json();}).then(data => {console.log("GeoJSON file data loaded:", data);displayGeoJSON(data);}).catch(error => console.error('Error loading GeoJSON file data:', error));}function loadSampleData() {fetch('/api/spatial/sample-geojson').then(response => {if (!response.ok) {return response.json().then(err => { throw new Error(err.error || `HTTP error! status: ${response.status}`) });}return response.json();}).then(data => {console.log("Sample GeoJSON data loaded:", data);displayGeoJSON(data);}).catch(error => console.error('Error loading sample GeoJSON data:', error));}// 默認加載示例數據// loadSampleData();</script></body>
    </html>
    
  2. 運行你的 Spring Boot 應用 (如果還沒運行的話)。

  3. 在瀏覽器中打開 http://localhost:8080/index.html

    你應該能看到一個地圖,并且可以通過點擊按鈕從你的 Spring Boot API 加載和顯示 GeoJSON 數據。

前端代碼解釋:

  • 引入 Leaflet 的 CSS 和 JS 文件。

  • 創建一個 div 作為地圖容器 (<div id="map"></div>)。

  • 初始化地圖 (L.map('map').setView(...)) 并添加一個 OpenStreetMap 的瓦片圖層。

  • displayGeoJSON(geoJsonData)
    

    函數:

    • 如果已存在 GeoJSON 圖層,則先移除。
    • 使用 L.geoJSON(geoJsonData, { onEachFeature: ... }) 將 GeoJSON 數據添加到地圖上。
    • onEachFeature 允許你為每個要素執行操作,例如綁定一個包含其屬性的彈出窗口 (bindPopup)。
    • map.fitBounds(...) 將地圖縮放到加載的數據的范圍。
  • loadShapefileData()loadGeoJSONFileData()loadSampleData() 函數:

    • 使用 fetch API 調用你的 Spring Boot 后端端點。
    • 獲取到 GeoJSON 響應后,調用 displayGeoJSON 來顯示數據。
    • 安全提示: loadShapefileDataloadGeoJSONFileData 中的 prompt 僅用于本地測試。在生產應用中,你不應該讓前端直接指定服務器上的任意文件路徑。應該設計 API,使其通過預定義的標識符或更安全的方式來獲取數據。

這就完成了你的第一個 GeoTools & Spring Boot 應用!你現在已經掌握了:

  • 設置項目和依賴。
  • 使用 GeoTools 讀取 Shapefile 和 GeoJSON。
  • 理解了 GeoTools 的基本要素處理概念。
  • 創建了一個 Spring Boot REST API 來提供 GeoJSON 數據。
  • (可選) 在 Leaflet 地圖上顯示了這些數據。

接下來,你可以探索更多 GeoTools 的功能,例如空間分析、坐標轉換、數據寫入、更復雜的數據庫交互等等。祝你學習愉快!

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

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

相關文章

深度解析String不可變性:從Java底層到設計哲學

一、String不可變性的直觀理解 在Java中,String對象一旦創建,其內容就不可更改。任何看似"修改"String的操作,實際上都是創建了一個全新的String對象。這種設計是Java語言基礎架構的重要部分,理解其底層原理對編寫高效、安全的Java程序至關重要。 String str =…

C++并發編程-2.C++ 線程管控

參考&#xff1a;https://llfc.club/category?catid225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2Tuk4RfvfBC788LlqnQrWiPiEGW 1. 簡歷 本節介紹C線程管控&#xff0c;包括移交線程的歸屬權&#xff0c;線程并發數量控制以及獲取線程id等基本操作。 2. 線程歸屬權 比如下面&#xff…

Qt面試常問

1.QT信號與槽的底層原理&#xff1f; 底層通過元對象系統和事件循環完成的&#xff0c;能夠在運行期間動態處理信號槽之間的連接與斷開&#xff0c;而不是像函數調用那樣在編譯期間就完全確定了。元對象系統包含&#xff1a;QObject類、Q_OBJECT宏定義、moc編譯器當發送一個信…

【git】錯誤

【成功解決】開代理 unable to access ‘https://github.com/laigeoffer/pmhub.git/’: Recv failure: Connection was reset

什么是狀態機?狀態機入門

狀態機&#xff1a;優雅管理復雜邏輯的Python實踐 在軟件開發中&#xff0c;狀態機&#xff08;Finite State Machine, FSM&#xff09; 是管理多狀態轉換的利器。它將行為分解為離散的狀態、事件和轉移規則&#xff0c;大幅提升代碼的可讀性與可維護性。本文通過Python示例解析…

【Python打卡Day41】簡單CNN@浙大疏錦行

可以看到即使在深度神經網絡情況下&#xff0c;準確率仍舊較差&#xff0c;這是因為特征沒有被有效提取----真正重要的是特征的提取和加工過程。MLP把所有的像素全部展平了&#xff08;這是全局的信息&#xff09;&#xff0c;無法布置到局部的信息&#xff0c;所以引入了卷積神…

MySQL中InnoDB存儲引擎底層原理與MySQL日志機制深入解析

MySQL的內部組件結構如下&#xff1a; 大體來說&#xff0c;MySQL 可以分為 Server 層和存儲引擎層兩部分。 Server層 主要包括連接器、查詢緩存、分析器、優化器、執行器等&#xff0c;涵蓋 MySQL 的大多數核心服務功能&#xff0c;以及所有的內置函數&#xff08;如日期、…

MCP基本概念

基本概念 現在大模型交互的熱門形式&#xff1a; 第一、Agent與Tools(工具)的交互Agent需要調用外部工具和APl、訪問數據庫、執行代碼等。> MCP 第二、Agent與Agent(其他智能體或用戶)的交互Agent需要理解其他Agent的意圖、協同完成任務、與用戶進行自然的對話。 > A2A…

Docker容器相關命令介紹和示例

Docker 容器是鏡像的運行實例。以下是常用的 Docker 容器命令及其示例&#xff1a; 1. 運行容器 docker run [選項] <鏡像名> [命令]常用選項&#xff1a; -d&#xff1a;后臺運行&#xff08;守護模式&#xff09;-it&#xff1a;交互式終端--name&#xff1a;指定容…

【Akshare】高效下載股票和ETF數據

在量化投資與金融數據分析的世界里&#xff0c;獲取高質量的市場數據是構建有效策略的關鍵。Python庫Akshare為我們提供了一個強大且易于使用的接口&#xff0c;可以輕松地從網絡上抓取各類金融數據。本文將詳細介紹如何利用Akshare下載股票和ETF的歷史行情數據。 安裝Akshare…

分布式--3--分布式事務

1 簡介 事務在單系統中的表現&#xff1a;多次數據庫操作用事務進行管理&#xff0c;來保證ACID原則。 但是如果各個模塊都是單獨獨立出來的微服務&#xff0c;進行了分布式部署&#xff0c;單系統里的事務將不能保證各個數據庫操作的一致性&#xff0c;因此就需要分布式事務來…

不同建模方式的介紹 RTL建模筆記(1)

說明&#xff1a;該專欄"RTL建模筆記"是《RTL Modeling with SystemVerilog for Simulation and Synthesis》的翻譯&#xff1b;該筆記略過了第一章第一小節中背景介紹內容&#xff0c;以及第二小節前面部分的門級、RTL級建模介紹&#xff0c;對于后續學習不影響。 …

<13>-MySQL用戶管理

目錄 一&#xff0c;用戶管理操作 1&#xff0c;創建用戶 2&#xff0c;查詢用戶 3&#xff0c;修改密碼 4&#xff0c;刪除用戶 二&#xff0c;數據庫權限 1&#xff0c;用戶授權 2&#xff0c;回收權限 一&#xff0c;用戶管理操作 1&#xff0c;創建用戶 --創建用戶…

如何使用超低噪聲電源提高AD 時鐘電路質量,改善超聲系統的圖像質量

超聲波技術是醫療診斷和其他應用中廣泛使用的無創工具&#xff0c;已經從靜態圖像進化到動態圖像&#xff0c;從黑白呈現變為彩色多普勒圖像。這些重大進步主要是由于引入了數字超聲技術。雖然這些進步提高了超聲成像的有效性和通用性&#xff0c;但同樣重要的是&#xff0c;這…

【解決方案】Kali 2022.3修復倉庫密鑰一鍵安裝docker,docker compose

1、Kali 2022.3 2、一鍵安裝docker&#xff0c;docker compose #!/bin/bashecho " 安全的Kali Docker安裝腳本 "# 備份重要配置 cp /etc/apt/sources.list /etc/apt/sources.list.backup.$(date %Y%m%d)# 修復Kali倉庫配置 echo "修復Kali倉庫配置..." ca…

Transformer、RNN (循環神經網絡) 和 CNN (卷積神經網絡)的區別

我們來詳細對比一下 Transformer、RNN (循環神經網絡) 和 CNN (卷積神經網絡) 這三種在深度學習中極其重要的架構&#xff0c;并通過具體例子說明它們的區別。 核心區別總結&#xff1a; 處理數據的方式&#xff1a; CNN: 專注于局部特征和空間/時間模式。通過卷積核在輸入數據…

408第二季 - 組成原理 - 數據類型轉換

這章內容會比較少 閑聊 如果題目說把8位改成4位&#xff0c;你保留低位就行了 這里保留的是0101 然后是有符號數和無符號數的轉換 機器數就是二進制長什么樣子 然后就是小數點是不參與存儲的 然后簡單看看代碼 這是short就說明是有符號數 unsigned就是說明是無符號數 然后y…

讓 Deepseek 寫電器電費計算器(html版本)

以下是一個簡單的電器電費計算器的HTML和CSS代碼&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

react_flow自定義節點、邊——使用darg布局樹狀結構

文章目錄 ?前言?引入react-flow?自定義節點nodeType?自定義邊edgeType?添加節點?inscode代碼塊?結束 ?前言 大家好&#xff0c;我是yma16&#xff0c;本文分享 前端 ——react_flow自定義節點、邊——使用darg布局樹狀結構。 自定義效果 可以自定義節點、邊、線條流動…

word表格批量轉excel,提取表格數據到excel

本文將帶你一步步實現一個將 Word 中的表格內容批量提取并轉換為 Excel 文件的自動化工具&#xff0c;適用于需要手動復制粘貼數據到excel的場景 假設我們有這樣的表格在word中&#xff0c;圖片世放在excel中便于截圖&#xff0c;現在需要將表格中有顏色的數據提取到對應的exce…