GeoJSON 是一種用 JSON 文件格式描述地圖的格式,2016 年 IETF 於 RFC 7946 規範了 GeoJSON 的規格。GeoJSON 的幾何物件有點:表示地理位置、線:表示街道公路邊界、多邊形:表示國家鄉鎮市界。
TopoJSON 是 GeoJSON 的擴充,TopoJSON 以一連串的點組合成的 Arcs 描述,line 與 polygon 都改用 arcs 描述,如果是邊界,在 TopoJSON 裡面的 arc 只會定義一次,這樣可有效減少文件的大小。
要將 TopoJSON 與 GeoJSON 文件互相轉換,可使用 node module
npm install topojson
npm install geojson
安裝後切換到 node_modules/topojson/node_modules/topojson-server/bin
這個目錄,可看到 geo2topo 指令
以下指令可將 GeoJSON 檔案轉換為 TopoJSON
./geo2topo towns-09007.geo.json > towns-09007.topo.json
切換到 node_modules/topojson/node_modules/topojson-client/bin
這個目錄,可看到 topo2geo 指令
這個指令可查詢 TopoJSON 裡面的地圖名稱
./topo2geo -l < towns-090007.topo.json
# towns-09007.geo
這邊會查詢到名稱為 towns-09007.geo
用以下指令將 TopoJSON 轉為 GeoJSON
./topo2geo towns-09007.geo=towns-090007-2.geo.json < towns-090007.topo.json
java jts library
以下節錄 GeoJSON 文件結構
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"id": "10005160",
"name": "三灣鄉"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
120.97453105516638,
24.583295428280817
],
[
120.96669830509721,
24.586708627549427
],
......
]
]
},
......
}
]
}
這邊使用了兩個 library: jts, jackson,jackson 是處理 JSON 文件,jts 是處理向量圖形的 library
<!-- https://github.com/locationtech/jts -->
<!-- https://mvnrepository.com/artifact/org.locationtech.jts/jts-core -->
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.19.0</version>
</dependency>
<dependency>
<groupId>org.locationtech.jts.io</groupId>
<artifactId>jts-io-common</artifactId>
<version>1.19.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.0</version>
</dependency>
透過 Jackson,將 FeatureCollection 裡面的 features array 分開,每一個獨立去查詢,GPS 點跟 每一個 feature 的 Polygon// 測試每一個 point 跟 polygon 的關係。
相關的 methods 有這些:
相等(Equals):幾何形狀拓撲上相等。
脫節(Disjoint):幾何形狀沒有共有的點。
相交(Intersects):幾何形狀至少有一個共有點(區別於脫節)
接觸(Touches):幾何形狀有至少一個公共的邊界點,但是沒有內部點。
交叉(Crosses):幾何形狀共享一些但不是所有的內部點。
內含(Within):幾何形狀A的線都在幾何形狀B內部。
包含(Contains):幾何形狀B的線都在幾何形狀A內部(區別於內含)
重疊(Overlaps):幾何形狀共享一部分但不是所有的公共點,而且相交處有他們自己相同的區域。
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.geojson.GeoJsonReader;
import java.io.File;
import java.io.IOException;
public class PointInsidePolygon {
public static void main(String[] args) throws IOException {
// https://blog.csdn.net/qq_36427942/article/details/129123733
// jackson lib to read json document
ObjectMapper mapper = new ObjectMapper();
ObjectNode geoJsonObject = (ObjectNode) mapper.readTree(new File("towns-10005.geo.json"));
// System.out.println("geoJsonObject.toString()="+geoJsonObject.toString());
// 透過 Jackson,將 FeatureCollection 裡面的 features array 分開
// 每一個獨立去查詢,GPS 點跟 每一個 feature 的 Polygon
// 測試每一個 point 跟 polygon 的關係
Coordinate GPSPint = new Coordinate(120.97453105516638,24.583295428280817);
JsonNode node2 = geoJsonObject.get("features");
// System.out.println("node2="+node2);
for(JsonNode node3: node2) {
// System.out.println("node3="+node3);
JsonNodeType node3Type = node3.getNodeType();
JsonNode node3PropertiesNode = node3.get("properties");
JsonNode node3PropertiesId = node3PropertiesNode.get("id");
JsonNode node3PropertiesName = node3PropertiesNode.get("name");
System.out.println("");
System.out.println("node3PropertiesId="+node3PropertiesId+", node3PropertiesName="+node3PropertiesName);
GeoJsonReader reader = new GeoJsonReader();
Geometry geometry = null;
try {
geometry = reader.read(node3.toString());
} catch (ParseException e) {
throw new RuntimeException(e);
}
String geometryType = geometry.getGeometryType();
// geometryType=GeometryCollection
System.out.println("geometryType="+geometryType+", length="+ geometry.getLength());
// Coordinate[] cors = geometry.getCoordinates();
// System.out.println("cors length="+cors.length);
// for(Coordinate c: cors) {
// System.out.println("c ="+c.toString() );
// }
// get ExteriorRing
if (geometry instanceof Polygon) {
geometry = ((Polygon) geometry).getExteriorRing();
} else {
System.err.println("Invalid Polygon");
return;
}
// JTS Geometry
GeometryFactory geometryFactory = new GeometryFactory();
Coordinate[] coordinates = geometry.getCoordinates();
Coordinate[] jtsCoordinates = new Coordinate[coordinates.length];
for (int i = 0; i < coordinates.length; i++) {
jtsCoordinates[i] = new Coordinate(coordinates[i].x, coordinates[i].y);
}
Polygon polygon = geometryFactory.createPolygon(jtsCoordinates);
// GPS Point
Point gpsPoint = geometryFactory.createPoint(GPSPint);
// 相等(Equals):幾何形狀拓撲上相等。
// 脫節(Disjoint):幾何形狀沒有共有的點。
// 相交(Intersects):幾何形狀至少有一個共有點(區別於脫節)
// 接觸(Touches):幾何形狀有至少一個公共的邊界點,但是沒有內部點。
// 交叉(Crosses):幾何形狀共享一些但不是所有的內部點。
// 內含(Within):幾何形狀A的線都在幾何形狀B內部。
// 包含(Contains):幾何形狀B的線都在幾何形狀A內部(區別於內含)
// 重疊(Overlaps):幾何形狀共享一部分但不是所有的公共點,而且相交處有他們自己相同的區域。
boolean isInside = polygon.contains(gpsPoint);
boolean isWithin = polygon.within(gpsPoint);
boolean intersects = polygon.intersects(gpsPoint);
boolean overlaps = polygon.overlaps(gpsPoint);
boolean crosses = polygon.crosses(gpsPoint);
boolean touches = polygon.touches(gpsPoint);
boolean disjoint = polygon.disjoint(gpsPoint);
System.out.println("gps "+gpsPoint);
System.out.println(" contains=" + isInside+", within=" + isWithin+", intersects="+intersects+". overlaps="+overlaps+", crosses="+crosses+", touches="+ touches+", disjoint="+disjoint);
}
}
}
執行結果如下:
node3PropertiesId="10005160", node3PropertiesName="三灣鄉"
geometryType=Polygon, length=0.4571892567423512
gps POINT (120.97453105516638 24.583295428280817)
contains=false, within=false, intersects=true. overlaps=false, crosses=false, touches=true, disjoint=false
node3PropertiesId="10005110", node3PropertiesName="南庄鄉"
geometryType=Polygon, length=0.6073270143853203
gps POINT (120.97453105516638 24.583295428280817)
contains=false, within=false, intersects=true. overlaps=false, crosses=false, touches=true, disjoint=false
node3PropertiesId="10005010", node3PropertiesName="苗栗市"
geometryType=Polygon, length=0.26286982385854196
gps POINT (120.97453105516638 24.583295428280817)
contains=false, within=false, intersects=false. overlaps=false, crosses=false, touches=false, disjoint=true
沒有留言:
張貼留言