From 9e3e21b8b1ae68d77c9799612ae255358c3d1a6d Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 30 Apr 2024 17:13:00 +0200 Subject: [PATCH] make zones attributable simplify / deduplicate code --- .../org/matsim/contrib/common/zones/Zone.java | 5 +- .../matsim/contrib/common/zones/ZoneImpl.java | 11 +++- .../contrib/common/zones/ZoneSystemUtils.java | 44 +++++++++---- .../zonal/DrtModeZonalSystemModule.java | 63 +------------------ 4 files changed, 45 insertions(+), 78 deletions(-) diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/Zone.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/Zone.java index 4a820fb6dbf..185c8927aab 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/zones/Zone.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/Zone.java @@ -4,12 +4,11 @@ import org.matsim.api.core.v01.BasicLocation; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Identifiable; -import org.matsim.api.core.v01.network.Link; +import org.matsim.utils.objectattributes.attributable.Attributable; import javax.annotation.Nullable; -import java.util.List; -public interface Zone extends BasicLocation, Identifiable { +public interface Zone extends BasicLocation, Identifiable, Attributable { @Nullable PreparedPolygon getPreparedGeometry(); diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneImpl.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneImpl.java index 9e327306915..1f08c3cd1e9 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneImpl.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneImpl.java @@ -3,11 +3,11 @@ import org.locationtech.jts.geom.prep.PreparedPolygon; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.network.Link; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.utils.objectattributes.attributable.Attributes; +import org.matsim.utils.objectattributes.attributable.AttributesImpl; import javax.annotation.Nullable; -import java.util.List; public class ZoneImpl implements Zone { @@ -17,6 +17,9 @@ public class ZoneImpl implements Zone { private final Coord centroid; private String type; + private final Attributes attributes = new AttributesImpl(); + + public ZoneImpl(Id id, PreparedPolygon preparedGeometry, @Nullable String type) { this(id, preparedGeometry, MGC.point2Coord(preparedGeometry.getGeometry().getCentroid()), type); } @@ -67,4 +70,8 @@ public static ZoneImpl createDummyZone(Id id, Coord centroid) { return new ZoneImpl(id, null, centroid, null); } + @Override + public Attributes getAttributes() { + return attributes; + } } diff --git a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java index 91f63ac8be0..725a9bb3a41 100644 --- a/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java +++ b/contribs/common/src/main/java/org/matsim/contrib/common/zones/ZoneSystemUtils.java @@ -5,6 +5,7 @@ import one.util.streamex.StreamEx; import org.apache.commons.lang3.tuple.Pair; import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygonal; import org.locationtech.jts.geom.prep.PreparedGeometry; import org.locationtech.jts.geom.prep.PreparedPolygon; import org.matsim.api.core.v01.Id; @@ -26,6 +27,9 @@ import org.matsim.contrib.common.zones.util.ZoneFinderImpl; import org.matsim.core.config.ConfigGroup; import org.matsim.core.utils.geometry.geotools.MGC; +import org.matsim.core.utils.gis.GeoFileReader; +import org.opengis.feature.Property; +import org.opengis.feature.simple.SimpleFeature; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -39,7 +43,6 @@ import java.util.stream.Collectors; import static java.util.stream.Collectors.*; -import static org.matsim.utils.gis.shp2matsim.ShpGeometryUtils.loadPreparedPolygons; /** * @author nkuehnel / MOIA @@ -47,6 +50,8 @@ public final class ZoneSystemUtils { + public static final String THE_GEOM = "the_geom"; + private ZoneSystemUtils() {} public static ZoneSystem createZoneSystem(Network network, ZoneSystemParams zoneSystemParams) { @@ -65,11 +70,9 @@ public static ZoneSystem createZoneSystem(@Nullable URL context, @Nonnull Networ case GISFileZoneSystemParams.SET_NAME -> { Preconditions.checkNotNull(((GISFileZoneSystemParams) zoneSystemParams).zonesShapeFile); Preconditions.checkNotNull(context); - - final List preparedGeometries = loadPreparedPolygons( - ConfigGroup.getInputFileURL(context, ((GISFileZoneSystemParams) zoneSystemParams).zonesShapeFile)); - yield ZoneSystemUtils.createFromPreparedGeometries(network, - EntryStream.of(preparedGeometries).mapKeys(i -> (i + 1) + "").toMap()); + URL url = ConfigGroup.getInputFileURL(context, ((GISFileZoneSystemParams) zoneSystemParams).zonesShapeFile); + Collection features = GeoFileReader.getAllFeatures(url); + yield ZoneSystemUtils.createFromFeatures(network, features); } case SquareGridZoneSystemParams.SET_NAME -> { Preconditions.checkNotNull(((SquareGridZoneSystemParams) zoneSystemParams).cellSize); @@ -90,23 +93,40 @@ public static ZoneSystem createZoneSystem(@Nullable URL context, @Nonnull Networ return zoneSystem; } - public static ZoneSystem createFromPreparedGeometries(Network network, Map geometries) { + public static ZoneSystem createFromFeatures(Network network, Collection features) { + + Map featureById = StreamEx.of(features.stream()) + .mapToEntry(SimpleFeature::getID, sf -> new PreparedFeature(sf, new PreparedPolygon((Polygonal) sf.getDefaultGeometry()))) + .toMap(); //geometries without links are skipped Map> linksByGeometryId = StreamEx.of(network.getLinks().values()) - .mapToEntry(l -> getGeometryIdForLink(l, geometries), l -> l) + .mapToEntry(l -> getGeometryIdForLink(l, featureById), l -> l) .filterKeys(Objects::nonNull) .grouping(toList()); //the zonal system contains only zones that have at least one link Map, Zone> zones = EntryStream.of(linksByGeometryId) - .mapKeyValue((id, links) -> new ZoneImpl(Id.create(id, Zone.class), geometries.get(id), null)) + .mapKeyValue((id, links) -> { + PreparedFeature preparedFeature = featureById.get(id); + ZoneImpl zone = new ZoneImpl(Id.create(id, Zone.class), preparedFeature.preparedPolygon, null); + for (Property attribute : preparedFeature.sf.getProperties()) { + String attributeName = attribute.getName().toString(); + Object att = preparedFeature.sf().getAttribute(attributeName); + if(!attributeName.equals(THE_GEOM) && att != null) { + zone.getAttributes().putAttribute(attributeName, att); + } + } + return zone; + }) .collect(IdCollectors.toIdMap(Zone.class, Identifiable::getId, zone -> zone)); return new ZoneSystemImpl(zones.values(), new ZoneFinderImpl(zones), network); } + private record PreparedFeature(SimpleFeature sf, PreparedPolygon preparedPolygon){} + /** * @param link * @return the the {@code PreparedGeometry} that contains the {@code linkId}. @@ -114,11 +134,11 @@ public static ZoneSystem createFromPreparedGeometries(Network network, Map geometries) { + private static String getGeometryIdForLink(Link link, Map features) { Point linkCoord = MGC.coord2Point(link.getToNode().getCoord()); - return geometries.entrySet() + return features.entrySet() .stream() - .filter(e -> e.getValue().intersects(linkCoord)) + .filter(e -> e.getValue().preparedPolygon.intersects(linkCoord)) .findAny() .map(Map.Entry::getKey) .orElse(null); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtModeZonalSystemModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtModeZonalSystemModule.java index 52f76e5b8db..3c0352b425a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtModeZonalSystemModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtModeZonalSystemModule.java @@ -20,32 +20,15 @@ package org.matsim.contrib.drt.analysis.zonal; -import com.google.common.base.Preconditions; -import one.util.streamex.EntryStream; -import org.locationtech.jts.geom.prep.PreparedGeometry; -import org.locationtech.jts.geom.prep.PreparedPolygon; import org.matsim.api.core.v01.network.Network; -import org.matsim.contrib.common.zones.Zone; import org.matsim.contrib.common.zones.ZoneSystem; import org.matsim.contrib.common.zones.ZoneSystemParams; import org.matsim.contrib.common.zones.ZoneSystemUtils; -import org.matsim.contrib.common.zones.systems.grid.GISFileZoneSystemParams; -import org.matsim.contrib.common.zones.systems.grid.h3.H3GridZoneSystemParams; -import org.matsim.contrib.common.zones.systems.grid.h3.H3ZoneSystem; -import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystem; -import org.matsim.contrib.common.zones.systems.grid.square.SquareGridZoneSystemParams; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; -import org.matsim.core.config.ConfigGroup; import org.matsim.core.controler.MatsimServices; -import java.util.List; -import java.util.function.Predicate; - -import static org.matsim.utils.gis.shp2matsim.ShpGeometryUtils.loadPreparedGeometries; -import static org.matsim.utils.gis.shp2matsim.ShpGeometryUtils.loadPreparedPolygons; - /** * @author Michal Maciejewski (michalm) */ @@ -63,53 +46,11 @@ public void install() { if (drtCfg.getZonalSystemParams().isPresent()) { DrtZoneSystemParams params = drtCfg.getZonalSystemParams().get(); ZoneSystemParams zoneSystemParams = params.getZoneSystemParams(); + String crs = getConfig().global().getCoordinateSystem(); bindModal(ZoneSystem.class).toProvider(modalProvider(getter -> { Network network = getter.getModal(Network.class); - switch (zoneSystemParams.getName()) { - case GISFileZoneSystemParams.SET_NAME: { - Preconditions.checkNotNull(((GISFileZoneSystemParams) zoneSystemParams).zonesShapeFile); - final List preparedGeometries = loadPreparedPolygons( - ConfigGroup.getInputFileURL(getConfig().getContext(), ((GISFileZoneSystemParams) zoneSystemParams).zonesShapeFile)); - return ZoneSystemUtils.createFromPreparedGeometries(network, - EntryStream.of(preparedGeometries).mapKeys(i -> (i + 1) + "").toMap()); - } - - case SquareGridZoneSystemParams.SET_NAME: { - Preconditions.checkNotNull(((SquareGridZoneSystemParams) zoneSystemParams).cellSize); - Predicate zoneFilter; - if(drtCfg.operationalScheme == DrtConfigGroup.OperationalScheme.serviceAreaBased) { - List serviceAreas = loadPreparedGeometries(ConfigGroup.getInputFileURL(getConfig().getContext(), - drtCfg.drtServiceAreaShapeFile)); - zoneFilter = zone -> serviceAreas.stream().anyMatch(serviceArea -> serviceArea.intersects(zone.getPreparedGeometry().getGeometry())); - } else { - zoneFilter = zone -> true; - } - - SquareGridZoneSystem squareGridZoneSystem = new SquareGridZoneSystem(network, ((SquareGridZoneSystemParams) zoneSystemParams).cellSize, zoneFilter); - return squareGridZoneSystem; - } - - case H3GridZoneSystemParams.SET_NAME: { - - Preconditions.checkNotNull(((H3GridZoneSystemParams) zoneSystemParams).h3Resolution); - String crs = getConfig().global().getCoordinateSystem(); - - Predicate zoneFilter; - if (drtCfg.operationalScheme == DrtConfigGroup.OperationalScheme.serviceAreaBased) { - List serviceAreas = loadPreparedGeometries(ConfigGroup.getInputFileURL(getConfig().getContext(), - drtCfg.drtServiceAreaShapeFile)); - zoneFilter = zone -> serviceAreas.stream().anyMatch(serviceArea -> serviceArea.intersects(zone.getPreparedGeometry().getGeometry())); - } else { - zoneFilter = zone -> true; - } - - return new H3ZoneSystem(crs, ((H3GridZoneSystemParams) zoneSystemParams).h3Resolution, network, zoneFilter); - } - - default: - throw new RuntimeException("Unsupported zone generation"); - } + return ZoneSystemUtils.createZoneSystem(getConfig().getContext(), network, zoneSystemParams, crs); })).asEagerSingleton(); bindModal(DrtZoneTargetLinkSelector.class).toProvider(modalProvider(getter -> {