From a335a391297d4f5e51c91a214ad1d87abbc82b0d Mon Sep 17 00:00:00 2001 From: Jeppe Krogh Date: Mon, 7 Sep 2026 13:32:23 +0200 Subject: [PATCH 1/4] Added OSM handicap parking dataset --- config/sources.yaml | 46 ++++++ src/Source/Osm/HandicapParking.php | 226 +++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 src/Source/Osm/HandicapParking.php diff --git a/config/sources.yaml b/config/sources.yaml index b011401..4324a7e 100644 --- a/config/sources.yaml +++ b/config/sources.yaml @@ -34,3 +34,49 @@ sources: oprettet_dato: 'Describes the register record.' rettet_dato: 'Describes the register record.' mi_style: 'MapInfo rendering style, empty throughout the export.' + + osm-handicap-parking: + title: 'Handicapparkering (OpenStreetMap), Aarhus Kommune' + description: >- + Disabled parking mapped in OpenStreetMap within Aarhus + Municipality: single reserved bays, and parking facilities that + state how many of their bays are reserved. Read via the Overpass + API. + publisher: 'OpenStreetMap contributors' + + # Community-maintained data; there is no single contact. + contact: ~ + landing_page: 'https://wiki.openstreetmap.org/wiki/Tag:parking_space%3Ddisabled' + + # The Overpass QL in the URL: within Aarhus Municipality (OSM relation + # 1784663), select every element tagged as a disabled parking space + # (parking_space=disabled) or as reserving bays for disabled parking + # (capacity:disabled, excluding "no" and "0"). + access_url: 'https://overpass-api.de/api/interpreter?data=%5Bout%3Ajson%5D%5Btimeout%3A180%5D%3Barea%283601784663%29-%3E.a%3B%28nwr%5B%22parking_space%22%3D%22disabled%22%5D%28area.a%29%3Bnwr%5B%22capacity%3Adisabled%22%5D%5B%22capacity%3Adisabled%22%21~%22%5E%28no%7C0%29%24%22%5D%28area.a%29%3B%29%3Bout%20geom%20tags%3B' + media_type: application/json + crs: 'EPSG:4326' + model: OnStreetParking + update_frequency: continuous + + # Republication must attribute "© OpenStreetMap contributors" and + # share under the same licence. + licence: 'https://opendatacommons.org/licenses/odbl/1-0/' + + # OpenStreetMap tagging is open-ended, so unlike a fixed-schema feed + # this cannot list everything an element may carry. These are the + # recurring tags in the current extract that are not published; + # coverage figures count records carrying the tag in the September + # 2026 extract. + omitted_fields: + amenity: 'Selector distinguishing a single bay (parking_space) from a facility (parking); the model carries no such distinction.' + capacity: 'Published for single bays only; on a facility it counts all bays and would overstate the reserved capacity.' + parking: 'Facility siting (street_side, surface, underground); every record is published under the one model the manifest names.' + orientation: 'How bays lie relative to the road; the model has no counterpart.' + disabled: 'Access restriction on street-side parking; redundant with the category every entity is published with.' + access: 'Who may enter; mapping it onto permit attributes needs an interpretation the tag values do not support.' + 'fee:conditional': 'Time-qualified refinement of fee; the category values the plain fee tag maps onto carry no schedule.' + surface: 'Paving material; the model has no counterpart. 6% of records carry it.' + wheelchair: 'Step-free access to the place, not the parking capacity. 3% of records carry it.' + 'capacity:charging': 'Bays with charging points; a different subset than the reserved bays this data set publishes. 2% of records carry it.' + operator: 'Who runs the facility; a fact about the business rather than its reserved bays. 1% of records carry it.' + brand: 'Commercial brand of the facility; the name already identifies it. Under 1% of records carry it.' diff --git a/src/Source/Osm/HandicapParking.php b/src/Source/Osm/HandicapParking.php new file mode 100644 index 0000000..f8fec01 --- /dev/null +++ b/src/Source/Osm/HandicapParking.php @@ -0,0 +1,226 @@ +catalog->get(self::KEY); + + // Overpass wraps the matched OSM objects in an envelope with version + // and timestamp metadata; the records live under `elements`. + foreach ($this->reader->read($source->accessUrl)['elements'] ?? [] as $element) { + if (\is_array($element) && null !== $entity = $this->toEntity($element, $source)) { + yield $entity; + } + } + } + + /** + * @param array $element Overpass JSON element + */ + private function toEntity(array $element, SourceDescriptor $source): ?NgsiEntity + { + $type = $element['type'] ?? null; + $id = $element['id'] ?? null; + + // OSM ids are only unique per element type, so both are needed to + // address the same object again on the next import. + if (!\is_string($type) || !\is_int($id)) { + return null; + } + + $geometry = $this->geometry($element); + if (null === $geometry) { + return null; + } + + $tags = \is_array($element['tags'] ?? null) ? $element['tags'] : []; + + $entity = new NgsiEntity( + \sprintf('urn:ngsi-ld:%s:aarhus-handicap-osm-%s-%d', $source->model, $type, $id), + $source->model + ); + + return $entity + ->property('name', trim((string) ($tags['name'] ?? ''))) + ->property('description', trim((string) ($tags['description'] ?? ''))) + ->property('category', $this->category($tags)) + ->property('totalSpotNumber', $this->reservedBays($tags)) + ->property('source', $source->accessUrl) + ->geoProperty('location', $this->transformer->geometry($source->crs, $geometry)); + } + + /** + * Every record is disabled parking; the fee tag refines that with the + * model's charging categories. Only its two plain values map — an + * untagged or unrecognised value states nothing about charging rather + * than assuming free. + * + * @param array $tags + * + * @return list + */ + private function category(array $tags): array + { + return match ($tags['fee'] ?? null) { + 'yes' => ['forDisabled', 'feeCharged'], + 'no' => ['forDisabled', 'free'], + default => ['forDisabled'], + }; + } + + /** + * Number of reserved bays the record carries. + * + * capacity:disabled counts them directly whatever the record is. A single + * bay (parking_space=disabled) is reserved in its entirety, so its own + * capacity applies — one when untagged, per the tag's definition. A + * facility's plain capacity counts all its bays and is never used, and + * capacity:disabled=yes states that reserved bays exist without counting + * them, so nothing is published for it. + * + * @param array $tags + */ + private function reservedBays(array $tags): ?int + { + if (null !== $count = $this->count($tags, 'capacity:disabled')) { + return $count; + } + + if ('disabled' === ($tags['parking_space'] ?? null)) { + return $this->count($tags, 'capacity') ?? 1; + } + + return null; + } + + /** + * @param array $tags + */ + private function count(array $tags, string $tag): ?int + { + $value = $tags[$tag] ?? null; + + return \is_string($value) && ctype_digit($value) ? (int) $value : null; + } + + /** + * @param array $element + * + * @return array{type: string, coordinates: mixed}|null + */ + private function geometry(array $element): ?array + { + return match ($element['type'] ?? null) { + 'node' => $this->point($element['lon'] ?? null, $element['lat'] ?? null), + 'way' => $this->wayGeometry($element['geometry'] ?? null), + // The feed's output mode carries no member geometry for + // relations, only their bounding box, so the centre of that box + // is the best location available. + 'relation' => $this->boundsCentre($element['bounds'] ?? null), + default => null, + }; + } + + /** + * @return array{type: string, coordinates: array{float, float}}|null + */ + private function point(mixed $longitude, mixed $latitude): ?array + { + if (!is_numeric($longitude) || !is_numeric($latitude)) { + return null; + } + + return ['type' => 'Point', 'coordinates' => [(float) $longitude, (float) $latitude]]; + } + + /** + * @return array{type: string, coordinates: mixed}|null + */ + private function wayGeometry(mixed $vertices): ?array + { + if (!\is_array($vertices)) { + return null; + } + + $positions = []; + foreach ($vertices as $vertex) { + if (!\is_array($vertex) || !is_numeric($vertex['lon'] ?? null) || !is_numeric($vertex['lat'] ?? null)) { + return null; + } + + $positions[] = [(float) $vertex['lon'], (float) $vertex['lat']]; + } + + // A way that returns to its first vertex outlines an area — here a + // bay or a parking lot — so it becomes a Polygon ring rather than a + // line along its edge. Four positions are a ring's minimum: three + // corners plus the repeated first. + if (\count($positions) >= 4 && $positions[0] === $positions[array_key_last($positions)]) { + return ['type' => 'Polygon', 'coordinates' => [$positions]]; + } + + if (\count($positions) >= 2) { + return ['type' => 'LineString', 'coordinates' => $positions]; + } + + return null; + } + + /** + * @return array{type: string, coordinates: array{float, float}}|null + */ + private function boundsCentre(mixed $bounds): ?array + { + if (!\is_array($bounds)) { + return null; + } + + foreach (['minlon', 'minlat', 'maxlon', 'maxlat'] as $edge) { + if (!is_numeric($bounds[$edge] ?? null)) { + return null; + } + } + + return $this->point( + ((float) $bounds['minlon'] + (float) $bounds['maxlon']) / 2, + ((float) $bounds['minlat'] + (float) $bounds['maxlat']) / 2 + ); + } +} From 443cc8d5b578733f484a31e3268c41fa7bce80ed Mon Sep 17 00:00:00 2001 From: Jeppe Krogh Date: Mon, 7 Sep 2026 13:32:32 +0200 Subject: [PATCH 2/4] Added tests --- tests/Source/Osm/HandicapParkingTest.php | 339 +++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 tests/Source/Osm/HandicapParkingTest.php diff --git a/tests/Source/Osm/HandicapParkingTest.php b/tests/Source/Osm/HandicapParkingTest.php new file mode 100644 index 0000000..dfd3e73 --- /dev/null +++ b/tests/Source/Osm/HandicapParkingTest.php @@ -0,0 +1,339 @@ +> */ + private array $entities; + + protected function setUp(): void + { + $catalog = new SourceCatalog(\dirname(__DIR__, 3).'/config/sources.yaml'); + $this->source = $catalog->get(self::KEY); + + $client = new MockHttpClient(function (string $method, string $url): MockResponse { + $this->requestedUrl = $url; + + return new MockResponse(json_encode($this->feed(), \JSON_THROW_ON_ERROR)); + }); + + $source = new HandicapParking(new FeedReader($client), new Wgs84Transformer(), $catalog); + + $this->entities = array_map( + static fn (NgsiEntity $entity): array => $entity->toArray(['https://example.com/context.jsonld']), + iterator_to_array($source->entities(), false) + ); + } + + public function testItReadsTheFeedTheManifestPointsAt(): void + { + $this->assertSame($this->source->accessUrl, $this->requestedUrl); + } + + public function testItSkipsRecordsWithoutAnIdentifierOrGeometry(): void + { + // Nine elements, of which one has no id and one no coordinates. + $this->assertCount(7, $this->entities); + } + + public function testItAddressesEntitiesByOsmTypeAndId(): void + { + // OSM ids are only unique per element type, so the type is part of + // the identifier; the osm marker keeps it clear of other data sets' + // aarhus-handicap ids. + $this->assertSame( + \sprintf('urn:ngsi-ld:%s:aarhus-handicap-osm-node-3580886094', $this->source->model), + $this->entities[0]['id'] + ); + $this->assertSame( + \sprintf('urn:ngsi-ld:%s:aarhus-handicap-osm-way-384028175', $this->source->model), + $this->entities[2]['id'] + ); + $this->assertSame( + \sprintf('urn:ngsi-ld:%s:aarhus-handicap-osm-relation-17151325', $this->source->model), + $this->entities[4]['id'] + ); + } + + public function testItTakesTheTypeFromTheManifestModel(): void + { + foreach ($this->entities as $entity) { + $this->assertSame($this->source->model, $entity['type']); + } + } + + public function testItMarksEveryEntityAsDisabledParking(): void + { + foreach ($this->entities as $entity) { + $this->assertContains('forDisabled', $entity['category']['value']); + } + } + + public function testItRefinesTheCategoryFromTheFeeTag(): void + { + // fee=yes and fee=no map onto the model's feeCharged and free + // categories; a record without the tag states nothing about charging. + $this->assertSame(['forDisabled', 'feeCharged'], $this->entities[0]['category']['value']); + $this->assertSame(['forDisabled', 'free'], $this->entities[4]['category']['value']); + $this->assertSame(['forDisabled'], $this->entities[2]['category']['value']); + } + + public function testItIgnoresAFeeValueItDoesNotRecognise(): void + { + // fee=donation neither confirms a charge nor rules one out. + $this->assertSame(['forDisabled'], $this->entities[6]['category']['value']); + } + + public function testItReadsTheReservedBayCountFromCapacityDisabled(): void + { + $this->assertSame(4, $this->entities[0]['totalSpotNumber']['value']); + + // Q-Park SHIP has capacity 299; only its three reserved bays count. + $this->assertSame(3, $this->entities[1]['totalSpotNumber']['value']); + } + + public function testItCountsASingleBayByItsOwnCapacity(): void + { + // A parking_space=disabled record is reserved in its entirety, so its + // capacity is the reserved count. + $this->assertSame(1, $this->entities[2]['totalSpotNumber']['value']); + $this->assertSame(3, $this->entities[3]['totalSpotNumber']['value']); + } + + public function testItCountsOneBayWhenASingleBayCarriesNoCapacity(): void + { + $this->assertSame(1, $this->entities[6]['totalSpotNumber']['value']); + } + + public function testItOmitsTheBayCountWhenOnlyItsExistenceIsTagged(): void + { + // capacity:disabled=yes; the facility's total capacity of 36 counts + // every bay and must not stand in for the reserved ones. + $this->assertArrayNotHasKey('totalSpotNumber', $this->entities[5]); + } + + public function testItPublishesTheNameWhenOneIsMapped(): void + { + $this->assertSame('Q-Park SHIP', $this->entities[1]['name']['value']); + $this->assertArrayNotHasKey('name', $this->entities[0]); + } + + public function testItPublishesTheDescriptionWhenOneIsMapped(): void + { + $this->assertSame('Ved hovedindgangen', $this->entities[6]['description']['value']); + $this->assertArrayNotHasKey('description', $this->entities[0]); + } + + public function testItPublishesANodeAsAPoint(): void + { + $location = $this->entities[0]['location']; + + $this->assertSame('GeoProperty', $location['type']); + $this->assertSame('Point', $location['value']['type']); + + // The feed is already WGS84, so the coordinates pass through + // unchanged — in GeoJSON order, longitude first. + $this->assertSame([10.2141175, 56.1540563], $location['value']['coordinates']); + } + + public function testItPublishesAClosedWayAsAPolygon(): void + { + $geometry = $this->entities[2]['location']['value']; + + $this->assertSame('Polygon', $geometry['type']); + + $ring = $geometry['coordinates'][0]; + $this->assertCount(5, $ring); + $this->assertSame($ring[0], $ring[4]); + $this->assertSame([10.2101549, 56.1572442], $ring[0]); + } + + public function testItPublishesAnOpenWayAsALineString(): void + { + $geometry = $this->entities[3]['location']['value']; + + $this->assertSame('LineString', $geometry['type']); + $this->assertSame( + [[10.1061762, 56.1832145], [10.1061762, 56.1832532]], + $geometry['coordinates'] + ); + } + + public function testItPublishesARelationAtTheCentreOfItsBounds(): void + { + // The feed's output mode gives a relation no member geometry, only a + // bounding box, so its centre stands in for the location. + $geometry = $this->entities[4]['location']['value']; + + $this->assertSame('Point', $geometry['type']); + $this->assertEqualsWithDelta(10.24793075, $geometry['coordinates'][0], 1e-9); + $this->assertEqualsWithDelta(56.08875175, $geometry['coordinates'][1], 1e-9); + } + + public function testItRecordsTheManifestUrlAsTheEntitySource(): void + { + $this->assertSame($this->source->accessUrl, $this->entities[0]['source']['value']); + } + + /** + * The first five elements are records from the live feed — a facility + * node, a named facility, a closed bay way, a bay way and a relation — + * kept verbatim except the second way, whose geometry is cut to two + * vertices to exercise the open-way path. The rest are constructed for + * the untagged capacity default, capacity:disabled=yes, an unrecognised + * fee value and the two guards that discard a record. + * + * @return array + */ + private function feed(): array + { + return [ + 'version' => 0.6, + 'generator' => 'Overpass API 0.7.62.11 87bfad18', + 'osm3s' => [ + 'timestamp_osm_base' => '2026-09-07T09:01:36Z', + 'timestamp_areas_base' => '2026-09-06T23:47:02Z', + 'copyright' => 'The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.', + ], + 'elements' => [ + [ + 'type' => 'node', + 'id' => 3580886094, + 'lat' => 56.1540563, + 'lon' => 10.2141175, + 'tags' => [ + 'access' => 'yes', + 'amenity' => 'parking', + 'capacity:disabled' => '4', + 'fee' => 'yes', + 'parking' => 'surface', + ], + ], + [ + 'type' => 'node', + 'id' => 12368170867, + 'lat' => 56.1676256, + 'lon' => 10.2255202, + 'tags' => [ + 'amenity' => 'parking', + 'brand' => 'Q-Park', + 'brand:wikidata' => 'Q1127798', + 'capacity' => '299', + 'capacity:disabled' => '3', + 'fee' => 'yes', + 'layer' => '-1', + 'name' => 'Q-Park SHIP', + 'operator' => 'Q-Park', + 'operator:type' => 'private', + 'operator:wikidata' => 'Q1127798', + 'parking' => 'underground', + ], + ], + [ + 'type' => 'way', + 'id' => 384028175, + 'bounds' => ['minlat' => 56.1572328, 'minlon' => 10.2101549, 'maxlat' => 56.1572932, 'maxlon' => 10.2102509], + 'geometry' => [ + ['lat' => 56.1572442, 'lon' => 10.2101549], + ['lat' => 56.1572328, 'lon' => 10.2102254], + ['lat' => 56.1572819, 'lon' => 10.2102509], + ['lat' => 56.1572932, 'lon' => 10.2101805], + ['lat' => 56.1572442, 'lon' => 10.2101549], + ], + 'tags' => [ + 'amenity' => 'parking_space', + 'capacity' => '1', + 'parking_space' => 'disabled', + ], + ], + [ + 'type' => 'way', + 'id' => 1180544298, + 'bounds' => ['minlat' => 56.1832145, 'minlon' => 10.1061762, 'maxlat' => 56.1832532, 'maxlon' => 10.1063419], + 'geometry' => [ + ['lat' => 56.1832145, 'lon' => 10.1061762], + ['lat' => 56.1832532, 'lon' => 10.1061762], + ], + 'tags' => [ + 'amenity' => 'parking_space', + 'capacity' => '3', + 'parking_space' => 'disabled', + ], + ], + [ + 'type' => 'relation', + 'id' => 17151325, + 'bounds' => ['minlat' => 56.0886708, 'minlon' => 10.2477857, 'maxlat' => 56.0888327, 'maxlon' => 10.2480758], + 'tags' => [ + 'access' => 'yes', + 'amenity' => 'parking', + 'capacity' => '11', + 'capacity:disabled' => '1', + 'fee' => 'no', + 'orientation' => 'perpendicular', + 'parking' => 'street_side', + 'surface' => 'asphalt', + 'type' => 'multipolygon', + ], + ], + [ + 'type' => 'node', + 'id' => 101, + 'lat' => 56.15, + 'lon' => 10.21, + 'tags' => [ + 'amenity' => 'parking', + 'capacity' => '36', + 'capacity:disabled' => 'yes', + ], + ], + [ + 'type' => 'node', + 'id' => 102, + 'lat' => 56.16, + 'lon' => 10.22, + 'tags' => [ + 'amenity' => 'parking_space', + 'parking_space' => 'disabled', + 'description' => 'Ved hovedindgangen', + 'fee' => 'donation', + ], + ], + [ + 'type' => 'node', + 'lat' => 56.17, + 'lon' => 10.23, + 'tags' => ['parking_space' => 'disabled'], + ], + [ + 'type' => 'node', + 'id' => 103, + 'tags' => ['parking_space' => 'disabled'], + ], + ], + ]; + } +} From 0f942ca818edc8f388ba72ed1297a2d15b8f7f5c Mon Sep 17 00:00:00 2001 From: Jeppe Krogh Date: Mon, 7 Sep 2026 13:33:34 +0200 Subject: [PATCH 3/4] Updated changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b089b68..e1eb7e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +* [#5](https://github.com/itk-dev/enter/pull/5) + * Added osm-handicap-parking. * [#3](https://github.com/itk-dev/enter/pull/3) * Import command that reads a geospatial feed, reprojects it to WGS84 and upserts it to an NGSI-LD broker. * A committed record per data set — feed, CRS, model, DCAT-AP metadata. From c85d5bc3f5ebd1ca049d14c50c95065e95977850 Mon Sep 17 00:00:00 2001 From: Jeppe Krogh Date: Tue, 8 Sep 2026 15:44:01 +0200 Subject: [PATCH 4/4] Adjust to changes from base branch --- config/sources.yaml | 1 + src/Source/Osm/HandicapParking.php | 24 ++++++++++++------------ tests/Source/Osm/HandicapParkingTest.php | 12 ++++++------ 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/config/sources.yaml b/config/sources.yaml index 13f78a0..2aa3d26 100644 --- a/config/sources.yaml +++ b/config/sources.yaml @@ -57,6 +57,7 @@ sources: media_type: application/json crs: 'EPSG:4326' model: OnStreetParking + context_url: 'https://raw.githubusercontent.com/smart-data-models/dataModel.Parking/master/context.jsonld' update_frequency: continuous # Republication must attribute "© OpenStreetMap contributors" and diff --git a/src/Source/Osm/HandicapParking.php b/src/Source/Osm/HandicapParking.php index f8fec01..3ab4f6d 100644 --- a/src/Source/Osm/HandicapParking.php +++ b/src/Source/Osm/HandicapParking.php @@ -6,9 +6,9 @@ use App\Geo\Wgs84Transformer; use App\Ngsi\NgsiEntity; -use App\Source\FeedReader; -use App\Source\SourceCatalog; -use App\Source\SourceDescriptor; +use App\Source\DataSourceReader; +use App\Source\Manifest\Catalog; +use App\Source\Manifest\Descriptor; use App\Source\SourceInterface; /** @@ -27,9 +27,9 @@ private const string KEY = 'osm-handicap-parking'; public function __construct( - private FeedReader $reader, + private DataSourceReader $reader, private Wgs84Transformer $transformer, - private SourceCatalog $catalog, + private Catalog $catalog, ) { } @@ -54,7 +54,7 @@ public function entities(): iterable /** * @param array $element Overpass JSON element */ - private function toEntity(array $element, SourceDescriptor $source): ?NgsiEntity + private function toEntity(array $element, Descriptor $source): ?NgsiEntity { $type = $element['type'] ?? null; $id = $element['id'] ?? null; @@ -78,12 +78,12 @@ private function toEntity(array $element, SourceDescriptor $source): ?NgsiEntity ); return $entity - ->property('name', trim((string) ($tags['name'] ?? ''))) - ->property('description', trim((string) ($tags['description'] ?? ''))) - ->property('category', $this->category($tags)) - ->property('totalSpotNumber', $this->reservedBays($tags)) - ->property('source', $source->accessUrl) - ->geoProperty('location', $this->transformer->geometry($source->crs, $geometry)); + ->setProperty('name', trim((string) ($tags['name'] ?? ''))) + ->setProperty('description', trim((string) ($tags['description'] ?? ''))) + ->setProperty('category', $this->category($tags)) + ->setProperty('totalSpotNumber', $this->reservedBays($tags)) + ->setProperty('source', $source->accessUrl) + ->geoProperty('location', $this->transformer->transformGeometry($source->crs, $geometry)); } /** diff --git a/tests/Source/Osm/HandicapParkingTest.php b/tests/Source/Osm/HandicapParkingTest.php index dfd3e73..e84df81 100644 --- a/tests/Source/Osm/HandicapParkingTest.php +++ b/tests/Source/Osm/HandicapParkingTest.php @@ -6,10 +6,10 @@ use App\Geo\Wgs84Transformer; use App\Ngsi\NgsiEntity; -use App\Source\FeedReader; +use App\Source\DataSourceReader; +use App\Source\Manifest\Catalog; +use App\Source\Manifest\Descriptor; use App\Source\Osm\HandicapParking; -use App\Source\SourceCatalog; -use App\Source\SourceDescriptor; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; @@ -22,7 +22,7 @@ class HandicapParkingTest extends TestCase { private const string KEY = 'osm-handicap-parking'; - private SourceDescriptor $source; + private Descriptor $source; private string $requestedUrl; @@ -31,7 +31,7 @@ class HandicapParkingTest extends TestCase protected function setUp(): void { - $catalog = new SourceCatalog(\dirname(__DIR__, 3).'/config/sources.yaml'); + $catalog = new Catalog(\dirname(__DIR__, 3).'/config/sources.yaml'); $this->source = $catalog->get(self::KEY); $client = new MockHttpClient(function (string $method, string $url): MockResponse { @@ -40,7 +40,7 @@ protected function setUp(): void return new MockResponse(json_encode($this->feed(), \JSON_THROW_ON_ERROR)); }); - $source = new HandicapParking(new FeedReader($client), new Wgs84Transformer(), $catalog); + $source = new HandicapParking(new DataSourceReader($client), new Wgs84Transformer(), $catalog); $this->entities = array_map( static fn (NgsiEntity $entity): array => $entity->toArray(['https://example.com/context.jsonld']),