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

目录
前言
一、面分割
二、面融合
三、捕捉
四、追踪构面
总结
前言
目前市面上大部分WebGIS的产品的编辑功能都很简陋,大部分都只支持简单节点编辑。稍微好一点的会支持面数据裁剪分割、融合。但是在大部分数据使用场景中,为避免出现矢量和矢量之间出现缝隙、压盖等拓扑错误,捕捉和追踪功能就非常重要了。本博客主要讲解如何通过postgis和go语言实现来实现这些功能。
一、面分割
面分割实现,主要是通过绘制的线来切割面数据,需要考虑的是如果线反复穿越面几何,面几何存在多环岛,且线穿过多环岛。这种逻辑如果是通过纯前端turf库是较难实现的。但是如果是通过postgis,就没有那么难实现了,废话不多说直接上代码:
// 图斑分割func (uc *UserController) SplitGeo(c *gin.Context) { var jsonData ZDList c.BindJSON(&jsonData) var pic []models.GeoPic DB := models.DB var Way TempWay DB.Where("tb_id = ?", jsonData.TBID).Find(&Way.TempGeo) if len(Way.TempGeo) == 0 { DB.Where("tb_id = ?", jsonData.TBID).Find(&Way.TempLayer) } DB.Where("tb_id = ?", jsonData.TBID).Find(&pic) var att models.TempLayerAttribute DB.Where("tb_id = ?", jsonData.TBID).Find(&att) //线切割面 line := Transformer.GetGeometryString(jsonData.Line.Features[0]) p := GetSingleGeo(jsonData.TBID) polygon := Transformer.GetGeometryString(p.Features[0]) 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) var geomData Transformer.GeometryData err := DB.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } t := p.Features[0].Type Properties := p.Features[0].Properties geo := PGBytesToGeojson(geomData) var NewGeo geojson.FeatureCollection for index, item := range geo.Geometry { var feature struct { Geometry map[string]interface{ } `json:"geometry"` Properties map[string]interface{ } `json:"properties"` Type string `json:"type"` } feature.Properties = Properties feature.Type = t feature.Geometry = item data2, _ := json.Marshal(feature) var myfeature *geojson.Feature json.Unmarshal(data2, &myfeature) //判断是图层还是调查 TBID := uuid.New().String() myfeature.ID = TBID myfeature.Properties["TBID"] = TBID //照片ID转移 for pi, _ := range pic { pic[pi].TBID = TBID } DB.Save(pic) if len(Way.TempGeo) >= 1 { myfeature.Properties["bsm"] = TBID myfeature.Properties["name"] = Way.TempGeo[0].Name + strconv.Itoa(index) newgeo := geojson.NewFeatureCollection() newgeo.Features = append(newgeo.Features, myfeature) geoJSONData, err := json.MarshalIndent(newgeo, "", " ") if err == nil { 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} 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} DB.Create(&result) DB.Create(&result_att) } } else { Layername := Way.TempLayer[0].Layername bsm := Way.TempLayer[0].BSM MAC := Way.TempLayer[0].MAC myfeature.Properties["name"] = Way.TempLayer[0].Name + strconv.Itoa(index) newgeo := geojson.NewFeatureCollection() newgeo.Features = append(newgeo.Features, myfeature) geoJSONData, err := json.MarshalIndent(newgeo, "", " ") if err == nil { result := models.TempLayer{ Layername: Layername, MAC: MAC, BSM: bsm, TBID: TBID, Geojson: geoJSONData, ZT: Way.TempLayer[0].ZT, Name: Way.TempLayer[0].Name} 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} DB.Create(&result_att) DB.Create(&result) } } NewGeo.Features = append(NewGeo.Features, myfeature) } DB.Delete(&att) //照片处理 //删除原图形 if len(Way.TempGeo) >= 1 { DB.Delete(&Way.TempGeo) } else { DB.Delete(&Way.TempLayer) } c.JSON(http.StatusOK, NewGeo)}
分割效果展示:
分割
二、面融合
面融合主要逻辑是需要提前判断两个面是否符合能够融合的条件,比如是否存在共边,是否空间存在重叠,所以需要提前使用ST_Intersects和ST_Touches进行判断。前端代码则需要写好交互逻辑,比如合并需要选择一个要素作为主要属性。
// 图斑合并type DissolverType struct { ZD []string `json:"ZD"` MainTBID string `json:"MainTBID"`}func (uc *UserController) DissolverGeo(c *gin.Context) { var jsonData DissolverType c.BindJSON(&jsonData) db := models.DB var Properties map[string]interface{ } var NewGeo geojson.FeatureCollection var t string var geolist []string var zds []*geojson.FeatureCollection for _, tt := range jsonData.ZD { zds = append(zds, GetSingleGeo(tt)) } var DelID []string for _, features := range zds { geo := Transformer.GetGeometryString(features.Features[0]) t = features.Features[0].Type if features.Features[0].Properties["TBID"] == jsonData.MainTBID { Properties = features.Features[0].Properties } else { DelID = append(DelID, features.Features[0].Properties["TBID"].(string)) } geolist = append(geolist, geo) } var sqlgeo string var geom []string var geom1 []string for index, item := range geolist { sql1 := fmt.Sprintf("%s AS ( SELECT ST_GeomFromGeoJSON('%s') AS geom),", "geo"+strconv.Itoa(index), item) sqlgeo = sqlgeo + sql1 geom = append(geom, "geo"+strconv.Itoa(index)+".geom") geom1 = append(geom1, "geo"+strconv.Itoa(index)) } result1 := strings.Join(geom, ",") result2 := strings.Join(geom1, ",") 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) var geomData Transformer.GeometryData err := db.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } var feature struct { Geometry map[string]interface{ } `json:"geometry"` Properties map[string]interface{ } `json:"properties"` Type string `json:"type"` } feature.Properties = Properties feature.Type = t json.Unmarshal(geomData.GeoJSON, &feature.Geometry) if feature.Geometry["geometries"] != nil { if len(feature.Geometry["geometries"].([]interface{ })) == 0 { c.String(http.StatusBadRequest, "err") return } } data2, _ := json.Marshal(feature) var myfeature *geojson.Feature json.Unmarshal(data2, &myfeature) myfeature.ID = jsonData.MainTBID NewGeo.Features = append(NewGeo.Features, myfeature) geoJSONData, _ := json.MarshalIndent(NewGeo, "", " ") var templayer []models.TempLayer db.Where("tb_id = ?", jsonData.MainTBID).Find(&templayer) if len(templayer) >= 1 { templayer[0].Geojson = geoJSONData db.Save(&templayer) } else { var templayer2 models.TempGeo db.Where("tb_id = ?", jsonData.MainTBID).Find(&templayer2) templayer2.Geojson = geoJSONData db.Save(&templayer2) } db.Where("tb_id IN (?)", DelID).Delete(&models.TempLayer{ }) db.Where("tb_id IN (?)", DelID).Delete(&models.TempGeo{ }) db.Where("tb_id IN (?)", DelID).Delete(&models.TempLayerAttribute{ }) c.JSON(http.StatusOK, NewGeo)}
融合效果:
合并
三、捕捉
捕捉的实现需要实现几个逻辑,第一个就是判断当前需要捕捉的图层,第二个是判断需要捕捉的对象的几何类型(点、线、面),第三个是需要判断捕捉的对象是矢量瓦片,还是geojson。代码如下,这里进行了6种情况的的判断并返回捕捉的点:
// 图形捕捉func (uc *UserController) Capture(c *gin.Context) { var jsonData CaptureType c.BindJSON(&jsonData) DB := models.DB Templarers := jsonData.TempLayer //临时图层同时存在 if len(Templarers) != 0 && jsonData.Layer != "" { var sql string geojsons := GetTempLayers(Templarers) //点线面分离 var PolygonJson []*geojson.Feature var LineJson []*geojson.Feature var PointJson []*geojson.Feature for _, item := range geojsons { switch item.Geometry.GeoJSONType() { case "Polygon": PolygonJson = append(PolygonJson, item) case "LineString": LineJson = append(LineJson, item) case "Point": PointJson = append(PointJson, item) } } //面数据捕捉 if len(PolygonJson) != 0 { geo := Transformer.GetFeatureString(PolygonJson) sql = fmt.Sprintf(`WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom),geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature),nearest_polygon AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS poly_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1),line_geom AS ( SELECT ST_Boundary(poly_geom) AS line_geom FROM nearest_polygon)SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distanceFROM input_point AS input, line_geom; `, jsonData.Point[0], jsonData.Point[1], geo) } //线数据捕捉 if len(LineJson) != 0 { geo := Transformer.GetFeatureString(LineJson) sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature ), nearest_line AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS line_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1 ) SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom, input.geom))::float AS distance FROM input_point AS input, nearest_line; `, jsonData.Point[0], jsonData.Point[1], geo) } //点数据捕捉 if len(PointJson) != 0 { geo := Transformer.GetFeatureString(PointJson) sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature ), nearest_point AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS point_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1 ) SELECT ST_AsGeoJSON(point_geom) AS geojson, ST_Distance(input.geom, point_geom)::float AS distance FROM input_point AS input, nearest_point; `, jsonData.Point[0], jsonData.Point[1], geo) } var geomData CaptureData err := DB.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } var feature struct { Geometry GeometryPoint `json:"geometry"` } json.Unmarshal(geomData.GeoJSON, &feature.Geometry) if geomData.Distance <= 0.00015 && len(feature.Geometry.Coordinates) != 0 { c.JSON(http.StatusOK, feature.Geometry.Coordinates) } else { layer := jsonData.Layer var schema []models.MySchema DB.Where("en = ?", layer).Find(&schema) if len(schema) >= 1 { switch schema[0].Type { case "polygon": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), nearest_polygon AS ( SELECT %s.geom AS poly_geom FROM %s, input_point AS input WHERE ST_Distance(input.geom, %s.geom) < 0.0005 ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ), line_geom AS ( SELECT ST_Boundary(poly_geom) AS line_geom FROM nearest_polygon ) SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance FROM input_point AS input, line_geom; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer) case "line": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), closest_line AS ( SELECT %s.geom AS line_geom FROM %s, input_point AS input WHERE ST_Distance(input.geom, %s.geom) < 0.0005 ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ) SELECT ST_AsGeoJSON(ST_ClosestPoint(closest_line.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(closest_line.line_geom, input.geom))::float AS distance FROM input_point AS input, closest_line; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer) case "point": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), nearest_point AS ( SELECT %s.geom AS nearest_geom FROM %s, input_point AS input ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ) SELECT ST_AsGeoJSON(nearest_geom) AS geojson, ST_Distance(input.geom, nearest_geom)::float AS distance FROM input_point AS input, nearest_point; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer) } } err := DB.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } json.Unmarshal(geomData.GeoJSON, &feature.Geometry) if geomData.Distance <= 0.00015 { c.JSON(http.StatusOK, feature.Geometry.Coordinates) } else { c.String(http.StatusBadRequest, "err") } } } else { var sql string if len(Templarers) != 0 && jsonData.Layer == "" { geojsons := GetTempLayers(Templarers) //点线面分离 var PolygonJson []*geojson.Feature var LineJson []*geojson.Feature var PointJson []*geojson.Feature for _, item := range geojsons { switch item.Geometry.GeoJSONType() { case "Polygon": PolygonJson = append(PolygonJson, item) case "LineString": LineJson = append(LineJson, item) case "Point": PointJson = append(PointJson, item) } } //面数据捕捉 if len(PolygonJson) != 0 { geo := Transformer.GetFeatureString(PolygonJson) sql = fmt.Sprintf(`WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom),geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature),nearest_polygon AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS poly_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1),line_geom AS ( SELECT ST_Boundary(poly_geom) AS line_geom FROM nearest_polygon)SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distanceFROM input_point AS input, line_geom; `, jsonData.Point[0], jsonData.Point[1], geo) } //线数据捕捉 if len(LineJson) != 0 { geo := Transformer.GetFeatureString(LineJson) sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature ), nearest_line AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS line_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1 ) SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom, input.geom))::float AS distance FROM input_point AS input, nearest_line; `, jsonData.Point[0], jsonData.Point[1], geo) } //点数据捕捉 if len(PointJson) != 0 { geo := Transformer.GetFeatureString(PointJson) sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), geojson_data AS ( SELECT jsonb_array_elements('%s'::jsonb) AS feature ), nearest_point AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS point_geom FROM geojson_data, input_point AS input WHERE ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) < 0.0005 ORDER BY ST_Distance(input.geom, ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326)) LIMIT 1 ) SELECT ST_AsGeoJSON(point_geom) AS geojson, ST_Distance(input.geom, point_geom)::float AS distance FROM input_point AS input, nearest_point; `, jsonData.Point[0], jsonData.Point[1], geo) } } else if len(Templarers) == 0 && jsonData.Layer != "" { layer := jsonData.Layer var schema []models.MySchema DB.Where("en = ?", layer).Find(&schema) if len(schema) >= 1 { switch schema[0].Type { case "polygon": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), nearest_polygon AS ( SELECT %s.geom AS poly_geom FROM %s, input_point AS input WHERE ST_Distance(input.geom, %s.geom) < 0.0005 ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ), line_geom AS ( SELECT ST_Boundary(poly_geom) AS line_geom FROM nearest_polygon ) SELECT ST_AsGeoJSON(ST_ClosestPoint(line_geom.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(line_geom.line_geom, input.geom))::float AS distance FROM input_point AS input, line_geom; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer) case "line": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), closest_line AS ( SELECT %s.geom AS line_geom FROM %s, input_point AS input WHERE ST_Distance(input.geom, %s.geom) < 0.0005 ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ) SELECT ST_AsGeoJSON(ST_ClosestPoint(closest_line.line_geom, input.geom)) AS geojson, ST_Distance(input.geom, ST_ClosestPoint(closest_line.line_geom, input.geom))::float AS distance FROM input_point AS input, closest_line; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer, jsonData.Layer) case "point": sql = fmt.Sprintf(` WITH input_point AS ( SELECT ST_SetSRID(ST_MakePoint(%f, %f), 4326) AS geom ), nearest_point AS ( SELECT %s.geom AS nearest_geom FROM %s, input_point AS input ORDER BY ST_Distance(input.geom, %s.geom) LIMIT 1 ) SELECT ST_AsGeoJSON(nearest_geom) AS geojson, ST_Distance(input.geom, nearest_geom)::float AS distance FROM input_point AS input, nearest_point; `, jsonData.Point[0], jsonData.Point[1], jsonData.Layer, jsonData.Layer, jsonData.Layer) } } } var geomData CaptureData err := DB.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } var feature struct { Geometry GeometryPoint `json:"geometry"` } json.Unmarshal(geomData.GeoJSON, &feature.Geometry) if geomData.Distance <= 0.00015 { c.JSON(http.StatusOK, feature.Geometry.Coordinates) } else { c.String(http.StatusBadRequest, "err") } }}
捕捉效果:
捕捉
四、追踪构面
追踪构面的实现逻辑可以说是最为复杂的,实现逻辑主要是根据绘制的线段和当前已经加载好了的图层进行空间分析,查询到最为合适的边界,并将该边界与绘制线进行构面。分解下来的逻辑大概是:
1、因为容差因素,需要将传入的线按照节点顺序进行两端延长,保证线会超过图层线并形成交点。
2、将延长线与几何图层进行空间分析,将与线有交集的几何提取出来
3、如果几何是面,需要转换为线要素
4、将输入线与相交线进行节点打散,并重新构造面
5、再将输入线与重新构造好的面进行线线叠加分析,找出重叠率最高的几何,并返回该几何
代码如下:
type AutoData struct { Line geojson.FeatureCollection `json:"Line"` Layer string TempLayer []string}func (uc *UserController) AutoPolygon(c *gin.Context) { var jsonData AutoData c.BindJSON(&jsonData) DB := models.DB var sql string line := Transformer.GetGeometryString(jsonData.Line.Features[0]) Templarers := jsonData.TempLayer if len(Templarers) != 0 { geojsons := GetTempLayers(Templarers) //点线面分离 var PolygonJson []*geojson.Feature var LineJson []*geojson.Feature for _, item := range geojsons { switch item.Geometry.GeoJSONType() { case "Polygon": PolygonJson = append(PolygonJson, item) case "LineString": LineJson = append(LineJson, item) } } //面数据捕捉 if len(PolygonJson) != 0 { geo := Transformer.GetFeatureString(PolygonJson) sql = fmt.Sprintf(` WITH input_linefrist AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom ), input_line AS ( SELECT ST_LineMerge(ST_Union( ARRAY[ ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry), geom, 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) ])) AS geom FROM input_linefrist ), intersecting_areas AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS geom FROM jsonb_array_elements('%s'::jsonb) AS feature WHERE ST_Intersects(ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326), (SELECT geom FROM input_line)) ), boundary_lines AS ( SELECT ST_Boundary(geom) AS geom FROM intersecting_areas ), closed_geometries AS ( SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines FROM boundary_lines ), newpolygons AS ( SELECT ST_Polygonize(lines) AS polygon_geoms FROM closed_geometries ), boundary_lines2 AS ( SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom FROM newpolygons ), intersecting_lines AS ( SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom FROM input_line, boundary_lines2 ), max_overlap AS ( SELECT boundary_geom, ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length FROM intersecting_lines ORDER BY overlap_length DESC LIMIT 1 ) SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson, overlap_length::float AS lenth FROM max_overlap `, line, geo) } if len(LineJson) != 0 { geo := Transformer.GetFeatureString(LineJson) sql = fmt.Sprintf(` WITH input_linefrist AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom ), input_line AS ( SELECT ST_LineMerge(ST_Union( ARRAY[ ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry), geom, 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) ])) AS geom FROM input_linefrist ), intersecting_areas AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326) AS geom FROM jsonb_array_elements('%s'::jsonb) AS feature WHERE ST_Intersects(ST_SetSRID(ST_GeomFromGeoJSON(feature->'geometry'::text), 4326), (SELECT geom FROM input_line)) ), boundary_lines AS ( SELECT geom AS geom FROM intersecting_areas ), closed_geometries AS ( SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines FROM boundary_lines ), newpolygons AS ( SELECT ST_Polygonize(lines) AS polygon_geoms FROM closed_geometries ), boundary_lines2 AS ( SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom FROM newpolygons ), intersecting_lines AS ( SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom FROM input_line, boundary_lines2 ), max_overlap AS ( SELECT boundary_geom, ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length FROM intersecting_lines ORDER BY overlap_length DESC LIMIT 1 ) SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson, overlap_length::float AS lenth FROM max_overlap `, line, geo) } } else { layer := jsonData.Layer var schema []models.MySchema DB.Where("en = ?", layer).Find(&schema) if len(schema) >= 1 { switch schema[0].Type { case "polygon": sql = fmt.Sprintf(` WITH input_linefrist AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom ), input_line AS ( SELECT ST_LineMerge(ST_Union( ARRAY[ ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry), geom, 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) ])) AS geom FROM input_linefrist ), intersecting_areas AS ( SELECT * FROM %s WHERE ST_Intersects(%s.geom, (SELECT geom FROM input_line)) ), boundary_lines AS ( SELECT ST_Boundary(geom) AS geom FROM intersecting_areas ), closed_geometries AS ( SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines FROM boundary_lines ), newpolygons AS ( SELECT ST_Polygonize(lines) AS polygon_geoms FROM closed_geometries ), boundary_lines2 AS ( SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom FROM newpolygons ), intersecting_lines AS ( SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom FROM input_line, boundary_lines2 ), max_overlap AS ( SELECT boundary_geom, ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length FROM intersecting_lines ORDER BY overlap_length DESC LIMIT 1 ) SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson, overlap_length::float AS lenth FROM max_overlap `, line, jsonData.Layer, jsonData.Layer) case "line": sql = fmt.Sprintf(` WITH input_linefrist AS ( SELECT ST_SetSRID(ST_GeomFromGeoJSON('%s'), 4326) AS geom ), input_line AS ( SELECT ST_LineMerge(ST_Union( ARRAY[ ST_MakeLine(ST_StartPoint(geom),ST_Project(ST_StartPoint(geom), 0.000001, ST_Azimuth(ST_PointN(geom,2),ST_StartPoint(geom)))::geometry), geom, 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) ])) AS geom FROM input_linefrist ), intersecting_areas AS ( SELECT * FROM %s WHERE ST_Intersects(%s.geom, (SELECT geom FROM input_line)) ), boundary_lines AS ( SELECT geom AS geom FROM intersecting_areas ), closed_geometries AS ( SELECT ST_Union(ST_Collect((SELECT geom FROM input_line), boundary_lines.geom)) AS lines FROM boundary_lines ), newpolygons AS ( SELECT ST_Polygonize(lines) AS polygon_geoms FROM closed_geometries ), boundary_lines2 AS ( SELECT (ST_Dump(ST_Boundary(polygon_geoms))).geom AS geom FROM newpolygons ), intersecting_lines AS ( SELECT ST_Intersection(input_line.geom, boundary_lines2.geom,0.0000001) AS geom, boundary_lines2.geom AS boundary_geom FROM input_line, boundary_lines2 WHERE ST_Intersects(input_line.geom, boundary_lines2.geom) ), max_overlap AS ( SELECT boundary_geom, ST_Length(geom)/ST_Length(boundary_geom) AS overlap_length FROM intersecting_lines ORDER BY overlap_length DESC LIMIT 1 ) SELECT ST_AsGeoJSON(ST_MakePolygon(boundary_geom)) AS geojson, overlap_length::float AS lenth FROM max_overlap `, line, jsonData.Layer, jsonData.Layer) } } } var geomData Transformer.GeometryData err := DB.Raw(sql).Scan(&geomData) if err.Error != nil { c.String(http.StatusBadRequest, "err") return } var feature struct { Geometry map[string]interface{ } `json:"geometry"` Properties map[string]interface{ } `json:"properties"` Type string `json:"type"` } feature.Type = "Feature" json.Unmarshal(geomData.GeoJSON, &feature.Geometry) feature.Properties = make(map[string]interface{ }) feature.Properties["name"] = "" var NewGeo geojson.FeatureCollection data2, _ := json.Marshal(feature) var myfeature *geojson.Feature aa := json.Unmarshal(data2, &myfeature) if aa != nil { fmt.Println(aa.Error()) } NewGeo.Features = append(NewGeo.Features, myfeature) c.JSON(http.StatusOK, NewGeo)}
追踪构面效果如下:
追踪
总结
这是我团队的移动端离线GIS产品《新源地图》的绘制模块的开发日志,该软件采用前后端部署一体式方案,将go语言开发的后端和postgis在安卓设备上进行编译。实现了在移动设备上能进行GIS分析、绘制功能。并通过动态瓦片技术实现了离线毫秒级加载千万级的矢量数据。如果对该产品有兴趣的同学可以在后台联系我。
分享让更多人看到