in baremaps-openstreetmap/src/main/java/org/apache/baremaps/openstreetmap/function/RelationMultiPolygonBuilder.java [85:175]
private void buildMultiPolygon(Relation relation) {
// Categorize the members of the relation by their role
var outerMembers = new ArrayList<Member>();
var innerMembers = new ArrayList<Member>();
var otherMembers = new ArrayList<Member>();
for (var member : relation.getMembers()) {
if (MemberType.WAY.equals(member.type())) {
switch (member.role()) {
case "outer" -> outerMembers.add(member);
case "inner" -> innerMembers.add(member);
default -> otherMembers.add(member);
}
}
}
// Prepare the outer polygons
var outerPolygons = createPolygons(outerMembers);
// Prepare the inner polygons
var innerPolygons = createPolygons(innerMembers);
innerPolygons = combinePolygons(innerPolygons);
// Prepare the other polygons
var otherPolygons = createPolygons(otherMembers);
otherPolygons = combinePolygons(otherPolygons);
// Categorize the other polygons as inner or outer
for (var otherPolygon : otherPolygons) {
// If the outer polygon contains the other polygon, it is an inner polygon
for (var outerPolygon : outerPolygons) {
if (outerPolygon.contains(otherPolygon)) {
innerPolygons.add(otherPolygon);
}
}
// Otherwise, it is an outer polygon
if (!innerPolygons.contains(otherPolygon)) {
outerPolygons.add(otherPolygon);
}
}
// Merge the outer and inner polygons to build the relation geometry
var polygons = new ArrayList<Polygon>();
// Iterate over the outer polygons
for (var outerPolygon : outerPolygons) {
// Initialize the shell and holes of the polygon
var shell = outerPolygon.getExteriorRing();
var holes = new ArrayList<LinearRing>();
for (int i = 0; i < outerPolygon.getNumInteriorRing(); i++) {
holes.add(outerPolygon.getInteriorRingN(i));
}
// Use a prepared geometry to speed up the contains operation
var preparedOuterPolygon = PreparedGeometryFactory.prepare(outerPolygon);
// Find to which inner polygon the outer polygon belongs
for (var innerPolygon : innerPolygons) {
if (preparedOuterPolygon.contains(innerPolygon)) {
// The exterior ring of the inner polygon is a hole in the outer polygon
holes.add(innerPolygon.getExteriorRing());
// The interior rings of the inner polygon are in fact additional polygons
for (int i = 0; i < innerPolygon.getNumInteriorRing(); i++) {
var innerPolygonHole =
GeometryUtils.GEOMETRY_FACTORY_WGS84
.createPolygon(innerPolygon.getInteriorRingN(i));
repairPolygon(innerPolygonHole, polygons);
}
}
}
// Build the polygon from the shell and holes
var polygon = GeometryUtils.GEOMETRY_FACTORY_WGS84.createPolygon(shell,
holes.toArray(new LinearRing[0]));
repairPolygon(polygon, polygons);
}
// Build the multipolygon from the polygons
if (!polygons.isEmpty()) {
var multiPolygon =
GeometryUtils.GEOMETRY_FACTORY_WGS84.createMultiPolygon(polygons.toArray(new Polygon[0]));
relation.setGeometry(multiPolygon);
} else {
var emptyMultiPolygon = GeometryUtils.GEOMETRY_FACTORY_WGS84.createMultiPolygon();
relation.setGeometry(emptyMultiPolygon);
}
}