问题
I have a number of spatial entities in a table, with a geometry
field called Boundaries
. I want to produce a GeoJson file with the simplified shapes/geometries.
This is my first attempt:
var entities = await db.Entities.ToListAsync();
dynamic geoJson = new ExpandoObject();
geoJson.type = "FeatureCollection";
var features = new List<dynamic>();
foreach (var entity in entities)
{
// simplify (uses SqlGeometry.Reduce)
var simplified = Utilities.Geo.Simplify(entity.Boundaries, tolerance);
// convert to GeoJSON4EntityFramework.Feature with the appropriate Id
var feature = Utilities.Geo.GetFeature(simplified, entity.Id);
features.Add(feature);
}
geoJson.features = features;
return geoJson;
The problem with the result is that, because the geometries are simplified individually, the boundaries aren't common, as shown below:
A second attempt is to combine the entities first, then simplify, then output as GeoJson:
var entities = await db.Entities.ToListAsync();
// bit of a hack to union all the boundaries
DbGeometry allBoundaries = null;
for (var i = 0; i < entities.Count; i++)
{
if (i == 0) allBoundaries = entities[i].Boundaries;
else allBoundaries = allBoundaries.Union(entities[i].Boundaries);
}
// simplify (uses SqlGeometry.Reduce)
var simplified = Utilities.Geo.Simplify(allBoundaries, tolerance);
dynamic geoJson = new ExpandoObject();
geoJson.type = "FeatureCollection";
var features = new List<dynamic>();
// convert to GeoJSON4EntityFramework.Feature with the (in)appropriate Id
var feature = Utilities.Geo.GetFeature(simplified, "ALL");
features.Add(feature);
geoJson.features = features;
return geoJson;
However, the .Union
is combining the entities into a single entity, despite what is said here that this doesn't happen. (I also then don't have the opportunity to put an Id on each feature, so just used 'ALL' for now). The result is the completely merged shape:
So the question is: how do I combine the boundaries across rows, then simplify, then produce as a feature collection, with each feature having the correct Id, as can be done in MapShaper (shown below)?
回答1:
Looks like this is not possible in SQL Server.
You need to convert the geometries to topologies, then simplify, then match back to the original geometries to preserve the properties/attributes/id/etc.
See: https://trac.osgeo.org/postgis/wiki/UsersWikiSimplifyWithTopologyExt
SQL Server doesn't have support for Topologies.
EDIT
I'm working on the code below, which converts polygons (not multipolygons) to linestrings, unions the linestrings to effectively get a topology layer, then simplifies that. It works really well, but the difficulty is not in converting the multilinestrings to multipolygons, which might need a tool like this.
select
geometry::STGeomFromText(replace(replace(e1.boundaries.STAsText(), 'POLYGON (', 'LINESTRING '), '))', ')'), 4326)
.STUnion(geometry::STGeomFromText(replace(replace(e2.boundaries.STAsText(), 'POLYGON (', 'LINESTRING '), '))', ')'), 4326))
.STUnion(geometry::STGeomFromText(replace(replace(e3.boundaries.STAsText(), 'POLYGON (', 'LINESTRING '), '))', ')'), 4326))
.Reduce(0.1)
from entities e1
cross join entities e2
cross join entities e3
where e1.code = 'dc7'
and e2.code = 'dc6'
and e3.code = 'dc8'
EDIT
Using NetTopologySuite, it can be done. I've written it up here. Using the Polygonizer
, you can convert the linestrings back to polygons. Then you have to match the polygons back to the originals by using a ratio of area intersection, and then (if matched) you can re-associate the properties.
来源:https://stackoverflow.com/questions/51755560/combine-union-and-simplify-reduce-dbgeometry-records-for-geojson