0


WebGIS开发之编辑功能(分割、融合、捕捉、追踪)


前言

目前市面上大部分WebGIS的产品的编辑功能都很简陋,大部分都只支持简单节点编辑。稍微好一点的会支持面数据裁剪分割、融合。但是在大部分数据使用场景中,为避免出现矢量和矢量之间出现缝隙、压盖等拓扑错误,捕捉和追踪功能就非常重要了。本博客主要讲解如何通过postgis和go语言实现来实现这些功能。


一、面分割

  1. 面分割实现,主要是通过绘制的线来切割面数据,需要考虑的是如果线反复穿越面几何,面几何存在多环岛,且线穿过多环岛。这种逻辑如果是通过纯前端turf库是较难实现的。但是如果是通过postgis,就没有那么难实现了,废话不多说直接上代码:
  1. // 图斑分割
  2. func (uc *UserController) SplitGeo(c *gin.Context) {
  3. var jsonData ZDList
  4. c.BindJSON(&jsonData)
  5. var pic []models.GeoPic
  6. DB := models.DB
  7. var Way TempWay
  8. DB.Where("tb_id = ?", jsonData.TBID).Find(&Way.TempGeo)
  9. if len(Way.TempGeo) == 0 {
  10. DB.Where("tb_id = ?", jsonData.TBID).Find(&Way.TempLayer)
  11. }
  12. DB.Where("tb_id = ?", jsonData.TBID).Find(&pic)
  13. var att models.TempLayerAttribute
  14. DB.Where("tb_id = ?", jsonData.TBID).Find(&att)
  15. //线切割面
  16. line := Transformer.GetGeometryString(jsonData.Line.Features[0])
  17. p := GetSingleGeo(jsonData.TBID)
  18. polygon := Transformer.GetGeometryString(p.Features[0])
  19. sql := fmt.Sprintf("WITH geom AS ( SELECT ST_GeomFromGeoJSON('%s') AS line, ST_GeomFromGeoJSON('%s' ) AS polygon) SELECT ST_AsGeoJSON(ST_Split(geom.polygon,geom.line)) AS geojson FROM geom", line, polygon)
  20. var geomData Transformer.GeometryData
  21. err := DB.Raw(sql).Scan(&geomData)
  22. if err.Error != nil {
  23. c.String(http.StatusBadRequest, "err")
  24. return
  25. }
  26. t := p.Features[0].Type
  27. Properties := p.Features[0].Properties
  28. geo := PGBytesToGeojson(geomData)
  29. var NewGeo geojson.FeatureCollection
  30. for index, item := range geo.Geometry {
  31. var feature struct {
  32. Geometry map[string]interface{} `json:"geometry"`
  33. Properties map[string]interface{} `json:"properties"`
  34. Type string `json:"type"`
  35. }
  36. feature.Properties = Properties
  37. feature.Type = t
  38. feature.Geometry = item
  39. data2, _ := json.Marshal(feature)
  40. var myfeature *geojson.Feature
  41. json.Unmarshal(data2, &myfeature)
  42. //判断是图层还是调查
  43. TBID := uuid.New().String()
  44. myfeature.ID = TBID
  45. myfeature.Properties["TBID"] = TBID
  46. //照片ID转移
  47. for pi, _ := range pic {
  48. pic[pi].TBID = TBID
  49. }
  50. DB.Save(pic)
  51. if len(Way.TempGeo) >= 1 {
  52. myfeature.Properties["bsm"] = TBID
  53. myfeature.Properties["name"] = Way.TempGeo[0].Name + strconv.Itoa(index)
  54. newgeo := geojson.NewFeatureCollection()
  55. newgeo.Features = append(newgeo.Features, myfeature)
  56. geoJSONData, err := json.MarshalIndent(newgeo, "", " ")
  57. if err == nil {
  58. result := models.TempGeo{TBID: TBID, Geojson: geoJSONData, MAC: Way.TempGeo[0].MAC, BSM: TBID, Date: time.Now().Format("2006-01-02 15:04:05"), Name: Way.TempGeo[0].Name + strconv.Itoa(index), ZT: Way.TempGeo[0].ZT}
  59. result_att := models.TempLayerAttribute{TBID: TBID, QKSM: att.QKSM, B: att.B, D: att.D, N: att.N, X: att.X, BZ: att.BZ, ZJR: att.ZJR, DCR: att.ZJR}
  60. DB.Create(&result)
  61. DB.Create(&result_att)
  62. }
  63. } else {
  64. Layername := Way.TempLayer[0].Layername
  65. bsm := Way.TempLayer[0].BSM
  66. MAC := Way.TempLayer[0].MAC
  67. myfeature.Properties["name"] = Way.TempLayer[0].Name + strconv.Itoa(index)
  68. newgeo := geojson.NewFeatureCollection()
  69. newgeo.Features = append(newgeo.Features, myfeature)
  70. geoJSONData, err := json.MarshalIndent(newgeo, "", " ")
  71. if err == nil {
  72. result := models.TempLayer{Layername: Layername, MAC: MAC, BSM: bsm, TBID: TBID, Geojson: geoJSONData, ZT: Way.TempLayer[0].ZT, Name: Way.TempLayer[0].Name}
  73. result_att := models.TempLayerAttribute{TBID: TBID, Layername: Layername, QKSM: att.QKSM, B: att.B, D: att.D, N: att.N, X: att.X, BZ: att.BZ, ZJR: att.ZJR, DCR: att.ZJR}
  74. DB.Create(&result_att)
  75. DB.Create(&result)
  76. }
  77. }
  78. NewGeo.Features = append(NewGeo.Features, myfeature)
  79. }
  80. DB.Delete(&att)
  81. //照片处理
  82. //删除原图形
  83. if len(Way.TempGeo) >= 1 {
  84. DB.Delete(&Way.TempGeo)
  85. } else {
  86. DB.Delete(&Way.TempLayer)
  87. }
  88. c.JSON(http.StatusOK, NewGeo)
  89. }

分割效果展示:

分割

二、面融合

面融合主要逻辑是需要提前判断两个面是否符合能够融合的条件,比如是否存在共边,是否空间存在重叠,所以需要提前使用ST_Intersects和ST_Touches进行判断。前端代码则需要写好交互逻辑,比如合并需要选择一个要素作为主要属性。

  1. // 图斑合并
  2. type DissolverType struct {
  3. ZD []string `json:"ZD"`
  4. MainTBID string `json:"MainTBID"`
  5. }
  6. func (uc *UserController) DissolverGeo(c *gin.Context) {
  7. var jsonData DissolverType
  8. c.BindJSON(&jsonData)
  9. db := models.DB
  10. var Properties map[string]interface{}
  11. var NewGeo geojson.FeatureCollection
  12. var t string
  13. var geolist []string
  14. var zds []*geojson.FeatureCollection
  15. for _, tt := range jsonData.ZD {
  16. zds = append(zds, GetSingleGeo(tt))
  17. }
  18. var DelID []string
  19. for _, features := range zds {
  20. geo := Transformer.GetGeometryString(features.Features[0])
  21. t = features.Features[0].Type
  22. if features.Features[0].Properties["TBID"] == jsonData.MainTBID {
  23. Properties = features.Features[0].Properties
  24. } else {
  25. DelID = append(DelID, features.Features[0].Properties["TBID"].(string))
  26. }
  27. geolist = append(geolist, geo)
  28. }
  29. var sqlgeo string
  30. var geom []string
  31. var geom1 []string
  32. for index, item := range geolist {
  33. sql1 := fmt.Sprintf("%s AS ( SELECT ST_GeomFromGeoJSON('%s') AS geom),", "geo"+strconv.Itoa(index), item)
  34. sqlgeo = sqlgeo + sql1
  35. geom = append(geom, "geo"+strconv.Itoa(index)+".geom")
  36. geom1 = append(geom1, "geo"+strconv.Itoa(index))
  37. }
  38. result1 := strings.Join(geom, ",")
  39. result2 := strings.Join(geom1, ",")
  40. sql := fmt.Sprintf("WITH %s is_adjacent AS ( SELECT ST_Intersects(%s) AS intersects , ST_Touches(%s) AS touches FROM %s) SELECT CASE WHEN intersects OR touches THEN ST_AsGeoJSON(ST_Union(%s)) ELSE ST_AsGeoJSON(ST_GeomFromGeoJSON('{\"type\": \"GeometryCollection\", \"geometries\": []}')) END AS geojson FROM %s,is_adjacent;", sqlgeo, result1, result1, result2, result1, result2)
  41. var geomData Transformer.GeometryData
  42. err := db.Raw(sql).Scan(&geomData)
  43. if err.Error != nil {
  44. c.String(http.StatusBadRequest, "err")
  45. return
  46. }
  47. var feature struct {
  48. Geometry map[string]interface{} `json:"geometry"`
  49. Properties map[string]interface{} `json:"properties"`
  50. Type string `json:"type"`
  51. }
  52. feature.Properties = Properties
  53. feature.Type = t
  54. json.Unmarshal(geomData.GeoJSON, &feature.Geometry)
  55. if feature.Geometry["geometries"] != nil {
  56. if len(feature.Geometry["geometries"].([]interface{})) == 0 {
  57. c.String(http.StatusBadRequest, "err")
  58. return
  59. }
  60. }
  61. data2, _ := json.Marshal(feature)
  62. var myfeature *geojson.Feature
  63. json.Unmarshal(data2, &myfeature)
  64. myfeature.ID = jsonData.MainTBID
  65. NewGeo.Features = append(NewGeo.Features, myfeature)
  66. geoJSONData, _ := json.MarshalIndent(NewGeo, "", " ")
  67. var templayer []models.TempLayer
  68. db.Where("tb_id = ?", jsonData.MainTBID).Find(&templayer)
  69. if len(templayer) >= 1 {
  70. templayer[0].Geojson = geoJSONData
  71. db.Save(&templayer)
  72. } else {
  73. var templayer2 models.TempGeo
  74. db.Where("tb_id = ?", jsonData.MainTBID).Find(&templayer2)
  75. templayer2.Geojson = geoJSONData
  76. db.Save(&templayer2)
  77. }
  78. db.Where("tb_id IN (?)", DelID).Delete(&models.TempLayer{})
  79. db.Where("tb_id IN (?)", DelID).Delete(&models.TempGeo{})
  80. db.Where("tb_id IN (?)", DelID).Delete(&models.TempLayerAttribute{})
  81. c.JSON(http.StatusOK, NewGeo)
  82. }

融合效果:

合并

三、捕捉

捕捉的实现需要实现几个逻辑,第一个就是判断当前需要捕捉的图层,第二个是判断需要捕捉的对象的几何类型(点、线、面),第三个是需要判断捕捉的对象是矢量瓦片,还是geojson。代码如下,这里进行了6种情况的的判断并返回捕捉的点:

  1. // 图形捕捉
  2. func (uc *UserController) Capture(c *gin.Context) {
  3. var jsonData CaptureType
  4. c.BindJSON(&jsonData)
  5. DB := models.DB
  6. Templarers := jsonData.TempLayer
  7. //临时图层同时存在
  8. if len(Templarers) != 0 && jsonData.Layer != "" {
  9. var sql string
  10. geojsons := GetTempLayers(Templarers)
  11. //点线面分离
  12. var PolygonJson []*geojson.Feature
  13. var LineJson []*geojson.Feature
  14. var PointJson []*geojson.Feature
  15. for _, item := range geojsons {
  16. switch item.Geometry.GeoJSONType() {
  17. case "Polygon":
  18. PolygonJson = append(PolygonJson, item)
  19. case "LineString":
  20. LineJson = append(LineJson, item)
  21. case "Point":
  22. PointJson = append(PointJson, item)
  23. }
  24. }
  25. //面数据捕捉
  26. if len(PolygonJson) != 0 {
  27. geo := Transformer.GetFeatureString(PolygonJson)
  28. sql = fmt.Sprintf(`
  29. WITH input_point AS (
  30. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  31. ),
  32. geojson_data AS (
  33. SELECT
  34. jsonb_array_elements('%s'::jsonb) AS feature
  35. ),
  36. nearest_polygon AS (
  37. SELECT
  38. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS poly_geom
  39. FROM
  40. geojson_data, input_point AS input
  41. WHERE
  42. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  43. ORDER BY
  44. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  45. LIMIT 1
  46. ),
  47. line_geom AS (
  48. SELECT
  49. ST_Boundary(poly_geom) AS line_geom
  50. FROM
  51. nearest_polygon
  52. )
  53. SELECT
  54. ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson,
  55. ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance
  56. FROM
  57. input_point AS input, line_geom;
  58. `, jsonData.Point[0], jsonData.Point[1], geo)
  59. }
  60. //线数据捕捉
  61. if len(LineJson) != 0 {
  62. geo := Transformer.GetFeatureString(LineJson)
  63. sql = fmt.Sprintf(`
  64. WITH input_point AS (
  65. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  66. ),
  67. geojson_data AS (
  68. SELECT
  69. jsonb_array_elements('%s'::jsonb) AS feature
  70. ),
  71. nearest_line AS (
  72. SELECT
  73. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS line_geom
  74. FROM
  75. geojson_data, input_point AS input
  76. WHERE
  77. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  78. ORDER BY
  79. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  80. LIMIT 1
  81. )
  82. SELECT
  83. ST_AsGeoJSON(ST_ClosestPoint(line_geom, input.geom)) AS geojson,
  84. ST_Distance(input.geom, ST_ClosestPoint(line_geom, input.geom))::float AS distance
  85. FROM
  86. input_point AS input, nearest_line;
  87. `, jsonData.Point[0], jsonData.Point[1], geo)
  88. }
  89. //点数据捕捉
  90. if len(PointJson) != 0 {
  91. geo := Transformer.GetFeatureString(PointJson)
  92. sql = fmt.Sprintf(`
  93. WITH input_point AS (
  94. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  95. ),
  96. geojson_data AS (
  97. SELECT
  98. jsonb_array_elements('%s'::jsonb) AS feature
  99. ),
  100. nearest_point AS (
  101. SELECT
  102. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS point_geom
  103. FROM
  104. geojson_data, input_point AS input
  105. WHERE
  106. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  107. ORDER BY
  108. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  109. LIMIT 1
  110. )
  111. SELECT
  112. ST_AsGeoJSON(point_geom) AS geojson,
  113. ST_Distance(input.geom, point_geom)::float AS distance
  114. FROM
  115. input_point AS input, nearest_point;
  116. `, jsonData.Point[0], jsonData.Point[1], geo)
  117. }
  118. var geomData CaptureData
  119. err := DB.Raw(sql).Scan(&geomData)
  120. if err.Error != nil {
  121. c.String(http.StatusBadRequest, "err")
  122. return
  123. }
  124. var feature struct {
  125. Geometry GeometryPoint `json:"geometry"`
  126. }
  127. json.Unmarshal(geomData.GeoJSON, &feature.Geometry)
  128. if geomData.Distance <= 0.00015 && len(feature.Geometry.Coordinates) != 0 {
  129. c.JSON(http.StatusOK, feature.Geometry.Coordinates)
  130. } else {
  131. layer := jsonData.Layer
  132. var schema []models.MySchema
  133. DB.Where("en = ?", layer).Find(&schema)
  134. if len(schema) >= 1 {
  135. switch schema[0].Type {
  136. case "polygon":
  137. sql = fmt.Sprintf(`
  138. WITH input_point AS (
  139. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  140. ),
  141. nearest_polygon AS (
  142. SELECT
  143. %s.geom AS poly_geom
  144. FROM
  145. %s, input_point AS input
  146. WHERE
  147. ST_Distance(input.geom, %s.geom) < 0.0005
  148. ORDER BY
  149. ST_Distance(input.geom, %s.geom)
  150. LIMIT 1
  151. ),
  152. line_geom AS (
  153. SELECT
  154. ST_Boundary(poly_geom) AS line_geom
  155. FROM
  156. nearest_polygon
  157. )
  158. SELECT
  159. ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson,
  160. ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance
  161. FROM
  162. input_point AS input, line_geom;
  163. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer)
  164. case "line":
  165. sql = fmt.Sprintf(`
  166. WITH input_point AS (
  167. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  168. ),
  169. closest_line AS (
  170. SELECT
  171. %s.geom AS line_geom
  172. FROM
  173. %s, input_point AS input
  174. WHERE
  175. ST_Distance(input.geom, %s.geom) < 0.0005
  176. ORDER BY
  177. ST_Distance(input.geom, %s.geom)
  178. LIMIT 1
  179. )
  180. SELECT
  181. ST_AsGeoJSON(ST_ClosestPoint(closest_line.line_geom, input.geom)) AS geojson,
  182. ST_Distance(input.geom, ST_ClosestPoint(closest_line.line_geom, input.geom))::float AS distance
  183. FROM
  184. input_point AS input, closest_line;
  185. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer)
  186. case "point":
  187. sql = fmt.Sprintf(`
  188. WITH input_point AS (
  189. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  190. ),
  191. nearest_point AS (
  192. SELECT
  193. %s.geom AS nearest_geom
  194. FROM
  195. %s, input_point AS input
  196. ORDER BY
  197. ST_Distance(input.geom, %s.geom)
  198. LIMIT 1
  199. )
  200. SELECT
  201. ST_AsGeoJSON(nearest_geom) AS geojson,
  202. ST_Distance(input.geom, nearest_geom)::float AS distance
  203. FROM
  204. input_point AS input, nearest_point;
  205. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer)
  206. }
  207. }
  208. err := DB.Raw(sql).Scan(&geomData)
  209. if err.Error != nil {
  210. c.String(http.StatusBadRequest, "err")
  211. return
  212. }
  213. json.Unmarshal(geomData.GeoJSON, &feature.Geometry)
  214. if geomData.Distance <= 0.00015 {
  215. c.JSON(http.StatusOK, feature.Geometry.Coordinates)
  216. } else {
  217. c.String(http.StatusBadRequest, "err")
  218. }
  219. }
  220. } else {
  221. var sql string
  222. if len(Templarers) != 0 && jsonData.Layer == "" {
  223. geojsons := GetTempLayers(Templarers)
  224. //点线面分离
  225. var PolygonJson []*geojson.Feature
  226. var LineJson []*geojson.Feature
  227. var PointJson []*geojson.Feature
  228. for _, item := range geojsons {
  229. switch item.Geometry.GeoJSONType() {
  230. case "Polygon":
  231. PolygonJson = append(PolygonJson, item)
  232. case "LineString":
  233. LineJson = append(LineJson, item)
  234. case "Point":
  235. PointJson = append(PointJson, item)
  236. }
  237. }
  238. //面数据捕捉
  239. if len(PolygonJson) != 0 {
  240. geo := Transformer.GetFeatureString(PolygonJson)
  241. sql = fmt.Sprintf(`
  242. WITH input_point AS (
  243. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  244. ),
  245. geojson_data AS (
  246. SELECT
  247. jsonb_array_elements('%s'::jsonb) AS feature
  248. ),
  249. nearest_polygon AS (
  250. SELECT
  251. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS poly_geom
  252. FROM
  253. geojson_data, input_point AS input
  254. WHERE
  255. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  256. ORDER BY
  257. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  258. LIMIT 1
  259. ),
  260. line_geom AS (
  261. SELECT
  262. ST_Boundary(poly_geom) AS line_geom
  263. FROM
  264. nearest_polygon
  265. )
  266. SELECT
  267. ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson,
  268. ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance
  269. FROM
  270. input_point AS input, line_geom;
  271. `, jsonData.Point[0], jsonData.Point[1], geo)
  272. }
  273. //线数据捕捉
  274. if len(LineJson) != 0 {
  275. geo := Transformer.GetFeatureString(LineJson)
  276. sql = fmt.Sprintf(`
  277. WITH input_point AS (
  278. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  279. ),
  280. geojson_data AS (
  281. SELECT
  282. jsonb_array_elements('%s'::jsonb) AS feature
  283. ),
  284. nearest_line AS (
  285. SELECT
  286. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS line_geom
  287. FROM
  288. geojson_data, input_point AS input
  289. WHERE
  290. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  291. ORDER BY
  292. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  293. LIMIT 1
  294. )
  295. SELECT
  296. ST_AsGeoJSON(ST_ClosestPoint(line_geom, input.geom)) AS geojson,
  297. ST_Distance(input.geom, ST_ClosestPoint(line_geom, input.geom))::float AS distance
  298. FROM
  299. input_point AS input, nearest_line;
  300. `, jsonData.Point[0], jsonData.Point[1], geo)
  301. }
  302. //点数据捕捉
  303. if len(PointJson) != 0 {
  304. geo := Transformer.GetFeatureString(PointJson)
  305. sql = fmt.Sprintf(`
  306. WITH input_point AS (
  307. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  308. ),
  309. geojson_data AS (
  310. SELECT
  311. jsonb_array_elements('%s'::jsonb) AS feature
  312. ),
  313. nearest_point AS (
  314. SELECT
  315. ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS point_geom
  316. FROM
  317. geojson_data, input_point AS input
  318. WHERE
  319. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005
  320. ORDER BY
  321. ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326))
  322. LIMIT 1
  323. )
  324. SELECT
  325. ST_AsGeoJSON(point_geom) AS geojson,
  326. ST_Distance(input.geom, point_geom)::float AS distance
  327. FROM
  328. input_point AS input, nearest_point;
  329. `, jsonData.Point[0], jsonData.Point[1], geo)
  330. }
  331. } else if len(Templarers) == 0 && jsonData.Layer != "" {
  332. layer := jsonData.Layer
  333. var schema []models.MySchema
  334. DB.Where("en = ?", layer).Find(&schema)
  335. if len(schema) >= 1 {
  336. switch schema[0].Type {
  337. case "polygon":
  338. sql = fmt.Sprintf(`
  339. WITH input_point AS (
  340. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  341. ),
  342. nearest_polygon AS (
  343. SELECT
  344. %s.geom AS poly_geom
  345. FROM
  346. %s, input_point AS input
  347. WHERE
  348. ST_Distance(input.geom, %s.geom) < 0.0005
  349. ORDER BY
  350. ST_Distance(input.geom, %s.geom)
  351. LIMIT 1
  352. ),
  353. line_geom AS (
  354. SELECT
  355. ST_Boundary(poly_geom) AS line_geom
  356. FROM
  357. nearest_polygon
  358. )
  359. SELECT
  360. ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson,
  361. ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance
  362. FROM
  363. input_point AS input, line_geom;
  364. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer)
  365. case "line":
  366. sql = fmt.Sprintf(`
  367. WITH input_point AS (
  368. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  369. ),
  370. closest_line AS (
  371. SELECT
  372. %s.geom AS line_geom
  373. FROM
  374. %s, input_point AS input
  375. WHERE
  376. ST_Distance(input.geom, %s.geom) < 0.0005
  377. ORDER BY
  378. ST_Distance(input.geom, %s.geom)
  379. LIMIT 1
  380. )
  381. SELECT
  382. ST_AsGeoJSON(ST_ClosestPoint(closest_line.line_geom, input.geom)) AS geojson,
  383. ST_Distance(input.geom, ST_ClosestPoint(closest_line.line_geom, input.geom))::float AS distance
  384. FROM
  385. input_point AS input, closest_line;
  386. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer)
  387. case "point":
  388. sql = fmt.Sprintf(`
  389. WITH input_point AS (
  390. SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom
  391. ),
  392. nearest_point AS (
  393. SELECT
  394. %s.geom AS nearest_geom
  395. FROM
  396. %s, input_point AS input
  397. ORDER BY
  398. ST_Distance(input.geom, %s.geom)
  399. LIMIT 1
  400. )
  401. SELECT
  402. ST_AsGeoJSON(nearest_geom) AS geojson,
  403. ST_Distance(input.geom, nearest_geom)::float AS distance
  404. FROM
  405. input_point AS input, nearest_point;
  406. `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer)
  407. }
  408. }
  409. }
  410. var geomData CaptureData
  411. err := DB.Raw(sql).Scan(&geomData)
  412. if err.Error != nil {
  413. c.String(http.StatusBadRequest, "err")
  414. return
  415. }
  416. var feature struct {
  417. Geometry GeometryPoint `json:"geometry"`
  418. }
  419. json.Unmarshal(geomData.GeoJSON, &feature.Geometry)
  420. if geomData.Distance <= 0.00015 {
  421. c.JSON(http.StatusOK, feature.Geometry.Coordinates)
  422. } else {
  423. c.String(http.StatusBadRequest, "err")
  424. }
  425. }
  426. }

捕捉效果:

捕捉

四、追踪构面

追踪构面的实现逻辑可以说是最为复杂的,实现逻辑主要是根据绘制的线段和当前已经加载好了的图层进行空间分析,查询到最为合适的边界,并将该边界与绘制线进行构面。分解下来的逻辑大概是:

1、因为容差因素,需要将传入的线按照节点顺序进行两端延长,保证线会超过图层线并形成交点。

2、将延长线与几何图层进行空间分析,将与线有交集的几何提取出来

3、如果几何是面,需要转换为线要素

4、将输入线与相交线进行节点打散,并重新构造面

5、再将输入线与重新构造好的面进行线线叠加分析,找出重叠率最高的几何,并返回该几何

代码如下:

  1. type AutoData struct {
  2. Line geojson.FeatureCollection `json:"Line"`
  3. Layer string
  4. TempLayer []string
  5. }
  6. func (uc *UserController) AutoPolygon(c *gin.Context) {
  7. var jsonData AutoData
  8. c.BindJSON(&jsonData)
  9. DB := models.DB
  10. var sql string
  11. line := Transformer.GetGeometryString(jsonData.Line.Features[0])
  12. Templarers := jsonData.TempLayer
  13. if len(Templarers) != 0 {
  14. geojsons := GetTempLayers(Templarers)
  15. //点线面分离
  16. var PolygonJson []*geojson.Feature
  17. var LineJson []*geojson.Feature
  18. for _, item := range geojsons {
  19. switch item.Geometry.GeoJSONType() {
  20. case "Polygon":
  21. PolygonJson = append(PolygonJson, item)
  22. case "LineString":
  23. LineJson = append(LineJson, item)
  24. }
  25. }
  26. //面数据捕捉
  27. if len(PolygonJson) != 0 {
  28. geo := Transformer.GetFeatureString(PolygonJson)
  29. sql = fmt.Sprintf(`
  30. WITH input_linefrist AS (
  31. SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom
  32. ),
  33. input_line AS (
  34. SELECT
  35. ST_LineMerge(ST_Union( ARRAY[
  36. ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry),
  37. geom,
  38. ST_MakeLine(ST_EndPoint(geom),ST_Project(ST_EndPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,ST_NumPoints(geom) - 1),ST_EndPoint(geom)))::geometry)
  39. ])) AS geom
  40. FROM input_linefrist
  41. ),
  42. intersecting_areas AS (
  43. SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS geom
  44. FROM jsonb_array_elements('%s'::jsonb) AS feature
  45. WHERE ST_Intersects(ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326), (SELECT geom FROM input_line))
  46. ),
  47. boundary_lines AS (
  48. SELECT ST_Boundary(geom) AS geom
  49. FROM intersecting_areas
  50. ),
  51. closed_geometries AS (
  52. SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines
  53. FROM boundary_lines
  54. ),
  55. newpolygons AS (
  56. SELECT ST_Polygonize(lines) AS polygon_geoms
  57. FROM closed_geometries
  58. ),
  59. boundary_lines2 AS (
  60. SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom
  61. FROM newpolygons
  62. ),
  63. intersecting_lines AS (
  64. SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom
  65. FROM input_line, boundary_lines2
  66. ),
  67. max_overlap AS (
  68. SELECT
  69. boundary_geom,
  70. ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length
  71. FROM intersecting_lines
  72. ORDER BY overlap_length DESC
  73. LIMIT 1
  74. )
  75. SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson,
  76. overlap_length::float AS lenth
  77. FROM max_overlap
  78. `, line, geo)
  79. }
  80. if len(LineJson) != 0 {
  81. geo := Transformer.GetFeatureString(LineJson)
  82. sql = fmt.Sprintf(`
  83. WITH input_linefrist AS (
  84. SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom
  85. ),
  86. input_line AS (
  87. SELECT
  88. ST_LineMerge(ST_Union( ARRAY[
  89. ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry),
  90. geom,
  91. ST_MakeLine(ST_EndPoint(geom),ST_Project(ST_EndPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,ST_NumPoints(geom) - 1),ST_EndPoint(geom)))::geometry)
  92. ])) AS geom
  93. FROM input_linefrist
  94. ),
  95. intersecting_areas AS (
  96. SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS geom
  97. FROM jsonb_array_elements('%s'::jsonb) AS feature
  98. WHERE ST_Intersects(ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326), (SELECT geom FROM input_line))
  99. ),
  100. boundary_lines AS (
  101. SELECT geom AS geom
  102. FROM intersecting_areas
  103. ),
  104. closed_geometries AS (
  105. SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines
  106. FROM boundary_lines
  107. ),
  108. newpolygons AS (
  109. SELECT ST_Polygonize(lines) AS polygon_geoms
  110. FROM closed_geometries
  111. ),
  112. boundary_lines2 AS (
  113. SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom
  114. FROM newpolygons
  115. ),
  116. intersecting_lines AS (
  117. SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom
  118. FROM input_line, boundary_lines2
  119. ),
  120. max_overlap AS (
  121. SELECT
  122. boundary_geom,
  123. ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length
  124. FROM intersecting_lines
  125. ORDER BY overlap_length DESC
  126. LIMIT 1
  127. )
  128. SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson,
  129. overlap_length::float AS lenth
  130. FROM max_overlap
  131. `, line, geo)
  132. }
  133. } else {
  134. layer := jsonData.Layer
  135. var schema []models.MySchema
  136. DB.Where("en = ?", layer).Find(&schema)
  137. if len(schema) >= 1 {
  138. switch schema[0].Type {
  139. case "polygon":
  140. sql = fmt.Sprintf(`
  141. WITH input_linefrist AS (
  142. SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom
  143. ),
  144. input_line AS (
  145. SELECT
  146. ST_LineMerge(ST_Union( ARRAY[
  147. ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry),
  148. geom,
  149. ST_MakeLine(ST_EndPoint(geom),ST_Project(ST_EndPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,ST_NumPoints(geom) - 1),ST_EndPoint(geom)))::geometry)
  150. ])) AS geom
  151. FROM input_linefrist
  152. ),
  153. intersecting_areas AS (
  154. SELECT *
  155. FROM %s
  156. WHERE ST_Intersects(%s.geom, (SELECT geom FROM input_line))
  157. ),
  158. boundary_lines AS (
  159. SELECT ST_Boundary(geom) AS geom
  160. FROM intersecting_areas
  161. ),
  162. closed_geometries AS (
  163. SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines
  164. FROM boundary_lines
  165. ),
  166. newpolygons AS (
  167. SELECT ST_Polygonize(lines) AS polygon_geoms
  168. FROM closed_geometries
  169. ),
  170. boundary_lines2 AS (
  171. SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom
  172. FROM newpolygons
  173. ),
  174. intersecting_lines AS (
  175. SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom
  176. FROM input_line, boundary_lines2
  177. ),
  178. max_overlap AS (
  179. SELECT
  180. boundary_geom,
  181. ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length
  182. FROM intersecting_lines
  183. ORDER BY overlap_length DESC
  184. LIMIT 1
  185. )
  186. SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson,
  187. overlap_length::float AS lenth
  188. FROM max_overlap
  189. `, line, jsonData.Layer, jsonData.Layer)
  190. case "line":
  191. sql = fmt.Sprintf(`
  192. WITH input_linefrist AS (
  193. SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom
  194. ),
  195. input_line AS (
  196. SELECT
  197. ST_LineMerge(ST_Union( ARRAY[
  198. ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry),
  199. geom,
  200. ST_MakeLine(ST_EndPoint(geom),ST_Project(ST_EndPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,ST_NumPoints(geom) - 1),ST_EndPoint(geom)))::geometry)
  201. ])) AS geom
  202. FROM input_linefrist
  203. ),
  204. intersecting_areas AS (
  205. SELECT *
  206. FROM %s
  207. WHERE ST_Intersects(%s.geom, (SELECT geom FROM input_line))
  208. ),
  209. boundary_lines AS (
  210. SELECT geom AS geom
  211. FROM intersecting_areas
  212. ),
  213. closed_geometries AS (
  214. SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines
  215. FROM boundary_lines
  216. ),
  217. newpolygons AS (
  218. SELECT ST_Polygonize(lines) AS polygon_geoms
  219. FROM closed_geometries
  220. ),
  221. boundary_lines2 AS (
  222. SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom
  223. FROM newpolygons
  224. ),
  225. intersecting_lines AS (
  226. SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom
  227. FROM input_line, boundary_lines2
  228. WHERE ST_Intersects(input_line.geom, boundary_lines2.geom)
  229. ),
  230. max_overlap AS (
  231. SELECT
  232. boundary_geom,
  233. ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length
  234. FROM intersecting_lines
  235. ORDER BY overlap_length DESC
  236. LIMIT 1
  237. )
  238. SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson,
  239. overlap_length::float AS lenth
  240. FROM max_overlap
  241. `, line, jsonData.Layer, jsonData.Layer)
  242. }
  243. }
  244. }
  245. var geomData Transformer.GeometryData
  246. err := DB.Raw(sql).Scan(&geomData)
  247. if err.Error != nil {
  248. c.String(http.StatusBadRequest, "err")
  249. return
  250. }
  251. var feature struct {
  252. Geometry map[string]interface{} `json:"geometry"`
  253. Properties map[string]interface{} `json:"properties"`
  254. Type string `json:"type"`
  255. }
  256. feature.Type = "Feature"
  257. json.Unmarshal(geomData.GeoJSON, &feature.Geometry)
  258. feature.Properties = make(map[string]interface{})
  259. feature.Properties["name"] = ""
  260. var NewGeo geojson.FeatureCollection
  261. data2, _ := json.Marshal(feature)
  262. var myfeature *geojson.Feature
  263. aa := json.Unmarshal(data2, &myfeature)
  264. if aa != nil {
  265. fmt.Println(aa.Error())
  266. }
  267. NewGeo.Features = append(NewGeo.Features, myfeature)
  268. c.JSON(http.StatusOK, NewGeo)
  269. }

追踪构面效果如下:

追踪


总结

这是我团队的移动端离线GIS产品《新源地图》的绘制模块的开发日志,该软件采用前后端部署一体式方案,将go语言开发的后端和postgis在安卓设备上进行编译。实现了在移动设备上能进行GIS分析、绘制功能。并通过动态瓦片技术实现了离线毫秒级加载千万级的矢量数据。如果对该产品有兴趣的同学可以在后台联系我。


本文转载自: https://blog.csdn.net/weixin_57664381/article/details/143287187
版权归原作者 努力的无空 所有, 如有侵权,请联系我们删除。

“WebGIS开发之编辑功能(分割、融合、捕捉、追踪)”的评论:

还没有评论