在台灣的政府公開資料中,許多地理資訊空間資料也常提供 WKT 的資料格式,因此今天帶大家來看一下 Android 上基本的 WKT 資料轉換。
Well-known Text (WKT) 是由開放地理空間協會 (OGC) 所訂定的純文字標記語言(text markup language),用來表示點、線、面等向量資料。
WKT 格式是一段字串,最前面會有大寫單字說明此 WKT 的類型,後面則是由括號包裹 X, Y 或是經緯度的經度、緯度。
地圖上的單點,以空格區分 X、Y。
POINT (120.556230978 24.29811730900007)
地圖上的線段,由 X、Y 組成單點,並以逗號 ,
區分各組點位。
LINESTRING (30 10, 10 30, 40 40)
地圖上的多邊形,由 X、Y 組成單點,並以逗號 ,
區分各組點位,多點間以括號群組多邊形點位。
可能只有單組 Polygon,也有可能有中間中空的情況,這時候,會是多組的群組點位,同樣以,
分組,第一組為此多邊形最外圍的點位,後面的組別則為中空的點位。
POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))
地圖上的多點,由 X、Y 組成單點,並以括號分組、逗號 ,
區分各組點位。
MULTIPOINT ((10 40), (40 30), (20 20), (30 10))
不相連的多組線段。
MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))
多組以上的 Polygon,基本上就是前面的 Polygon 的變形,就是將前面 Polygon 的 WKT 去除前面的類型文字後,以括號逗號分組排列。
圖片來源:維基百科
MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))
基本上 WKT 的解析,可視為單純的字串拆解,所以在本文的範例,是挑較為複雜的多邊形 Polygon
來跟大家分享。
以下的程式碼,可以用來解析 MULTIPOLYGON
與 POLYGON
的 WKT,並將其轉成多個 PolygonData
,PolygonData
內以 List<LatLng>
儲存外圍多邊形與中間空白的多邊形點位。
// 多邊形空洞
data class HoleList(
var holes: List<LatLng> = listOf()
)
// 多邊形
data class PolygonData(
var multiPolygon: List<LatLng> = listOf(),
var holeLists: MutableList<HoleList> = mutableListOf()
)
// 解析
private fun parsePolygon(wktString: String?): List<PolygonData> {
val areaList = mutableListOf<PolygonData>()
if (wktString == null) return areaList
var polygonObject: PolygonData
val wktSpilt: Array<String>
val wktStr: String
if (wktString.startsWith("POLYGON")) {
wktStr = wktString.replace("POLYGON", "")
wktSpilt = wktStr.substring(1).split("\\|".toRegex()).toTypedArray()
} else {
wktStr = wktString.replace("MULTIPOLYGON", "")
wktSpilt = wktStr.substring(1).split("\\)\\).\\(\\(".toRegex()).toTypedArray()
}
for (wkt in wktSpilt) {
val boundList: MutableList<LatLng> = mutableListOf()
// 內容有")("代表包含hole,第一組為區域範圍
if (wkt.contains("),(")) {
val coordinates = wkt.split("\\),\\(".toRegex()).toTypedArray()
val bounds = coordinates[0].replace("(", "").replace(")", "").split(",".toRegex())
.toTypedArray()
for (bound in bounds) try {
boundList.add(
LatLng(
bound.split(" ".toRegex()).toTypedArray()[1].toDouble(),
bound.split(" ".toRegex()).toTypedArray()[0].toDouble()
)
)
} catch (e: NumberFormatException) {
Log.e(TAG, "Polygon parser error(bound) : $bound")
}
polygonObject = PolygonData(boundList)
// 第一組以後全部為hole
for (h in 1 until coordinates.size) {
val latLngList = mutableListOf<LatLng>()
val holes = coordinates[h].replace(")", "").split(",".toRegex()).toTypedArray()
for (hole in holes) try {
latLngList.add(
LatLng(
hole.split(" ".toRegex()).toTypedArray()[1].toDouble(),
hole.split(" ".toRegex()).toTypedArray()[0].toDouble()
)
)
} catch (e: NumberFormatException) {
Log.e(TAG, "Polygon parser error(hole) : $hole")
}
polygonObject.addHoles(HoleList(latLngList))
}
} else {
val coordinates =
wkt.replace("(", "").replace(")", "").split(",".toRegex()).toTypedArray()
for (coordinate in coordinates) boundList.add(
LatLng(
coordinate.split(" ".toRegex()).toTypedArray()[1].toDouble(),
coordinate.split(" ".toRegex()).toTypedArray()[0].toDouble()
)
)
polygonObject = PolygonData(boundList)
}
areaList.add(polygonObject)
}
return areaList
}
MULTIPOLYGON(((120.556230978 24.29811730900007,120.556173968 24.29787322400005,120.55588675 24.29782493100003,120.55580922 24.29818251100005,120.555868604 24.29819630200006,120.556230978 24.29811730900007)))
以上就是今天的內容,有任何問題歡迎留言討論~
明天見囉!!