datex2-xml2json 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datex2_xml2json/__init__.py +0 -0
- datex2_xml2json/json_validator.py +36 -0
- datex2_xml2json/merge.py +39 -0
- datex2_xml2json/schema/Baustellen-4-00-00.sch +7 -0
- datex2_xml2json/schema/DATEXIISchema_2_2_0.xsd +4707 -0
- datex2_xml2json/schema/DATEXIISchema_2_2_3.xsd +6783 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/.DS_Store +0 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_AfirEnergyInfrastructure.json +2455 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_AfirFacilities.json +2809 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_Common.json +3651 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_CommonExtension.json +919 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_ControlledZone.json +1070 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_D2Payload.json +11 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_EnergyInfrastructure.json +1924 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_Facilities.json +2858 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_FaultAndStatus.json +1415 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_LocationExtension.json +186 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_LocationReferencing.json +4847 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_Parking.json +5314 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_ReroutingManagementEnhanced.json +1160 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_RoadTrafficData.json +1938 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_Situation.json +8040 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_TrafficManagementPlan.json +2358 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_TrafficRegulation.json +3377 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_UrbanExtensions.json +194 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/DATEXII_3_Vms.json +2544 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/.DS_Store +0 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_AfirEnergyInfrastructure.xsd +2102 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_AfirFacilities.xsd +2060 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_Common.xsd +4152 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_CommonExtension.xsd +1102 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_ControlledZone.xsd +601 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_D2Payload.xsd +280 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_EnergyInfrastructure.xsd +1761 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_Facilities.xsd +2037 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_FaultAndStatus.xsd +1076 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_LocationExtension.xsd +196 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_LocationReferencing.xsd +4132 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_Parking.xsd +4025 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_ReroutingManagementEnhanced.xsd +997 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_RoadTrafficData.xsd +1273 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_Situation.xsd +5777 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_TrafficManagementPlan.xsd +1184 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_TrafficRegulation.xsd +2611 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_UrbanExtensions.xsd +148 -0
- datex2_xml2json/schema/DATEXII_3.7_Profile/xml/DATEXII_3_Vms.xsd +2290 -0
- datex2_xml2json/schema/MDM_BaustellenInfoDigitalRoadworksProfile_04-03-00.xsd +2401 -0
- datex2_xml2json/schema/MDM_Datenmodell_Baustellen_04-00-00.xsd +3232 -0
- datex2_xml2json/schema/cifs_schema.json +161 -0
- datex2_xml2json/transform/.DS_Store +0 -0
- datex2_xml2json/transform/__init__.py +13 -0
- datex2_xml2json/transform/base.py +79 -0
- datex2_xml2json/transform/cifs_transformer.py +275 -0
- datex2_xml2json/transform/geojson_geometry_transformer.py +159 -0
- datex2_xml2json/transform/geoparquet_transformer.py +160 -0
- datex2_xml2json/transform/jsonata_transformer.py +100 -0
- datex2_xml2json/transform/roads_regio_transformer.py +88 -0
- datex2_xml2json/transform/source_setter.py +42 -0
- datex2_xml2json/transform/transformation_rules.py +526 -0
- datex2_xml2json/transformer.py +197 -0
- datex2_xml2json/util.py +141 -0
- datex2_xml2json-0.0.1.dist-info/METADATA +176 -0
- datex2_xml2json-0.0.1.dist-info/RECORD +65 -0
- datex2_xml2json-0.0.1.dist-info/WHEEL +4 -0
- datex2_xml2json-0.0.1.dist-info/licenses/LICENSE.md +179 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Callable
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class TransformationRules:
|
|
10
|
+
renamings: dict[str, str]
|
|
11
|
+
value_transforms: dict[str, Callable]
|
|
12
|
+
keys_to_drop: dict[str, list[str]]
|
|
13
|
+
type_mappings: dict[str, str]
|
|
14
|
+
keys_to_pull_up: dict[str, None | list[str]]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def push_down_vms_configuration(vms: dict) -> dict:
|
|
18
|
+
vmsConfiguration = {}
|
|
19
|
+
if 'numberOfPictogramDisplayAreas' in vms:
|
|
20
|
+
vmsConfiguration['numberOfDisplayAreas'] = vms.pop('numberOfPictogramDisplayAreas')
|
|
21
|
+
if 'vmsPictogramDisplayCharacteristics' in vms:
|
|
22
|
+
displays = vms.pop('vmsPictogramDisplayCharacteristics')
|
|
23
|
+
vmsConfiguration['displayArea'] = [
|
|
24
|
+
{
|
|
25
|
+
'displayArea': {'vmsPictogramDisplayArea': displayArea.get('vmsPictogramDisplayCharacteristics')},
|
|
26
|
+
'displayAreaIndex': displayArea.pop('displayAreaIndex'),
|
|
27
|
+
}
|
|
28
|
+
for displayArea in displays
|
|
29
|
+
if 'vmsPictogramDisplayCharacteristics' in displayArea
|
|
30
|
+
]
|
|
31
|
+
if len(vmsConfiguration) > 0:
|
|
32
|
+
vms['vmsConfiguration'] = vmsConfiguration
|
|
33
|
+
return vms
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
INFRASTRUCTURE_DESCRIPTOR_VALUES = {
|
|
37
|
+
'atMotorwayInterchange',
|
|
38
|
+
'atRestArea',
|
|
39
|
+
'atServiceArea',
|
|
40
|
+
'atTollPlaza',
|
|
41
|
+
'atTunnelEntryOrExit',
|
|
42
|
+
'inGallery',
|
|
43
|
+
'inTunnel',
|
|
44
|
+
'onBridge',
|
|
45
|
+
'onConnector',
|
|
46
|
+
'onElevatedSection',
|
|
47
|
+
'onFlyover',
|
|
48
|
+
'onIceRoad',
|
|
49
|
+
'onLevelCrossing',
|
|
50
|
+
'onLinkRoad',
|
|
51
|
+
'onRoundabout',
|
|
52
|
+
'onTheRoadway',
|
|
53
|
+
'onUndergroundSection',
|
|
54
|
+
'onUnderpass',
|
|
55
|
+
'withinJunction',
|
|
56
|
+
}
|
|
57
|
+
POSITION_ON_CARRIAGEWAY_VALUES = {
|
|
58
|
+
'onTheLeft',
|
|
59
|
+
'onTheRight',
|
|
60
|
+
'inTheCentre',
|
|
61
|
+
}
|
|
62
|
+
GEOGRAPHIC_CHARACATERISTIC_VALUES = {
|
|
63
|
+
'aroundABendInRoad',
|
|
64
|
+
'onBorder',
|
|
65
|
+
'onPass',
|
|
66
|
+
'overCrestOfHill',
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def transform_supplementaryPositionalDescription(v: dict):
|
|
71
|
+
# locationDescriptior needs to be split in multiple properties
|
|
72
|
+
if 'locationDescriptor' in v:
|
|
73
|
+
locationDescriptors = v.pop('locationDescriptor')
|
|
74
|
+
for ld_value in locationDescriptors:
|
|
75
|
+
if ld_value in INFRASTRUCTURE_DESCRIPTOR_VALUES:
|
|
76
|
+
if 'infrastructureDescriptor' in v:
|
|
77
|
+
logger.warning(
|
|
78
|
+
'Conflicting values for infrastructureDescriptor (%s vs %s), keeping former.',
|
|
79
|
+
v['infrastructureDescriptor'],
|
|
80
|
+
ld_value,
|
|
81
|
+
)
|
|
82
|
+
continue
|
|
83
|
+
v['infrastructureDescriptor'] = {'value': ld_value}
|
|
84
|
+
if ld_value in POSITION_ON_CARRIAGEWAY_VALUES:
|
|
85
|
+
if 'positionOnCarriageway' in v:
|
|
86
|
+
logger.warning(
|
|
87
|
+
'Conflicting values for positionOnCarriageway (%s vs %s), keeping former.',
|
|
88
|
+
v['positionOnCarriageway'],
|
|
89
|
+
ld_value,
|
|
90
|
+
)
|
|
91
|
+
continue
|
|
92
|
+
v['positionOnCarriageway'] = {'value': ld_value}
|
|
93
|
+
if ld_value in GEOGRAPHIC_CHARACATERISTIC_VALUES:
|
|
94
|
+
if 'geographicDescriptor' in v:
|
|
95
|
+
logger.warning(
|
|
96
|
+
'Conflicting values for geographicDescriptor (%s vs %s), keeping former.',
|
|
97
|
+
v['geographicDescriptor'],
|
|
98
|
+
ld_value,
|
|
99
|
+
)
|
|
100
|
+
continue
|
|
101
|
+
v['geographicDescriptor'] = {'value': ld_value}
|
|
102
|
+
|
|
103
|
+
if 'carriageway' in v:
|
|
104
|
+
for c in v['carriageway']:
|
|
105
|
+
lengthAffected = c.pop('lengthAffected', None)
|
|
106
|
+
if lengthAffected is not None:
|
|
107
|
+
return v | {'lengthAffected': float(lengthAffected)}
|
|
108
|
+
|
|
109
|
+
return v
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def wrap_lroffset(lr_dict: dict):
|
|
113
|
+
new_dict = {}
|
|
114
|
+
for k in lr_dict:
|
|
115
|
+
if k == 'openlrPositiveOffset':
|
|
116
|
+
new_dict['openlrOffsets'] = {k: lr_dict[k]}
|
|
117
|
+
else:
|
|
118
|
+
new_dict[k] = lr_dict[k]
|
|
119
|
+
return new_dict
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def transform_alertc(v: dict, shouldWrapWithValue: bool):
|
|
123
|
+
"""
|
|
124
|
+
In v2, alertCDirection did not distinguish between encoding and affected direction.
|
|
125
|
+
We assume, as apparently NDW mandated explictly, that for encoding matches affected direction and,
|
|
126
|
+
in case of both or unknown affected direction, encoding is positive.
|
|
127
|
+
"""
|
|
128
|
+
currentAlertCDirectionCoded = v['alertCDirectionCoded']
|
|
129
|
+
if 'alertCDirectionCoded' in v:
|
|
130
|
+
v['alertCAffectedDirection'] = 'aligned' if currentAlertCDirectionCoded in ['positive', 'negative'] else v['alertCDirectionCoded']
|
|
131
|
+
v['alertCDirectionCoded'] = 'positive' if currentAlertCDirectionCoded in ['both', 'unknown'] else v['alertCDirectionCoded']
|
|
132
|
+
if shouldWrapWithValue:
|
|
133
|
+
v['alertCAffectedDirection'] = {'value': v['alertCAffectedDirection']}
|
|
134
|
+
v['alertCDirectionCoded'] = {'value': v['alertCDirectionCoded']}
|
|
135
|
+
|
|
136
|
+
return v
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
DEFAULT_LANGUAGE = 'de'
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class TransformationRuleFactory:
|
|
143
|
+
ACCIDENT_TYPE_MAPPINGS = {
|
|
144
|
+
'seriousAccident': 'seriousInjuryOrFatalAccident',
|
|
145
|
+
'multivehicleAccident': 'multipleVehicleAccident',
|
|
146
|
+
'earlierAccident': 'accident', # TODO: perhaps this should be another situationRecord?
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
CAUSE_TYPE_MAPPINGS = {
|
|
150
|
+
'shearWeightOfTraffic': 'other',
|
|
151
|
+
'earlierAccident': 'earlierIncident',
|
|
152
|
+
'congestion': 'abnormalTraffic',
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
VEHICLE_OBSTRUCTION_TYPE_MAPPINGS = {
|
|
156
|
+
'brokenDownBus': 'brokenDownVehicle',
|
|
157
|
+
'brokenDownHeavyLorry': 'brokenDownVehicle',
|
|
158
|
+
'slowMovingMaintenanceVehicle': 'slowVehicle',
|
|
159
|
+
'trackLayingVehicle': 'trackedVehicle',
|
|
160
|
+
'vehicleStuckUnderBridge': 'vehicleStuck',
|
|
161
|
+
'saltingOrGrittingVehicleInUse': 'other',
|
|
162
|
+
'snowplough': 'other',
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# see https://docs.ndw.nu/en/dataformaten/datex2-v2.3/conversie-naar-v3/#generalobstruction-maps-to-generalobstruction
|
|
166
|
+
OBSTRUCTION_TYPE_MAPPINGS = {
|
|
167
|
+
'recklessDriver': 'other',
|
|
168
|
+
'houseFire': 'other',
|
|
169
|
+
'highSpeedChase': 'other',
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
CAR_PARK_STATUS_TO_SERVICE_TYPE_MAPPINGS = {
|
|
173
|
+
'allCarParksFull': 'noParkingAvailability',
|
|
174
|
+
'carParkFull': 'noParkingAvailability',
|
|
175
|
+
'carParkClosed': 'noParkingAvailability',
|
|
176
|
+
'noMoreParkingSpacesAvailable': 'noParkingAvailability',
|
|
177
|
+
'serviceAreaFull': 'serviceAreaOvercrowdedDriveToAnotherServiceArea',
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Type mappings convert { ..., "@xsi:type": "<key>" } to { "<value>": { ... }}
|
|
181
|
+
datex_xml_2_json_xsitype_mappings = {
|
|
182
|
+
'AbnormalTraffic': 'sitAbnormalTraffic',
|
|
183
|
+
'Accident': 'sitAccident',
|
|
184
|
+
'AnimalPresenceObstruction': 'sitAnimalPresenceObstruction',
|
|
185
|
+
'AuthorityOperation': 'sitAuthorityOperation',
|
|
186
|
+
'Conditions': 'sitConditions',
|
|
187
|
+
'ConstructionWorks': 'sitConstructionWorks',
|
|
188
|
+
'DisturbanceActivity': 'sitDisturbanceActivity',
|
|
189
|
+
'EnvironmentalObstruction': 'sitEnvironmentalObstruction',
|
|
190
|
+
'EquipmentOrSystemFault': 'sitEquipmentOrSystemFault',
|
|
191
|
+
'GeneralObstruction': 'sitGeneralObstruction',
|
|
192
|
+
'GeneralInstructionOrMessageToRoadUsers': 'sitGeneralInstructionOrMessageToRoadUsers',
|
|
193
|
+
'GeneralNetworkManagement': 'sitGeneralNetworkManagement',
|
|
194
|
+
'GenericSituationRecord': 'sitGenericSituationRecord',
|
|
195
|
+
'InfrastructureDamageObstruction': 'sitInfrastructureDamageObstruction',
|
|
196
|
+
'MaintenanceWorks': 'sitMaintenanceWorks',
|
|
197
|
+
'NonWeatherRelatedRoadConditions': 'sitNonWeatherRelatedRoadConditions',
|
|
198
|
+
'PoorEnvironmentConditions': 'sitPoorEnvironmentConditions',
|
|
199
|
+
'PublicEvent': 'sitPublicEvent',
|
|
200
|
+
'RoadOperatorServiceDisruption': 'sitRoadOperatorServiceDisruption',
|
|
201
|
+
'RoadOrCarriagewayOrLaneManagement': 'sitRoadOrCarriagewayOrLaneManagement',
|
|
202
|
+
'RoadsideAssistance': 'sitRoadsideAssistance',
|
|
203
|
+
'ReroutingManagement': 'sitReroutingManagement',
|
|
204
|
+
'WeatherRelatedRoadConditions': 'sitWeatherRelatedRoadConditions',
|
|
205
|
+
'ServiceDisruption': 'sitServiceDisruption',
|
|
206
|
+
# see for CarParks https://docs.ndw.nu/en/dataformaten/datex2-v2.3/conversie-naar-v3/#carparks-maps-to-servicedisruption
|
|
207
|
+
'CarParks': 'sitServiceDisruption',
|
|
208
|
+
# TODO: need to set serviceDisruptionType
|
|
209
|
+
'RoadsideServiceDisruption': 'sitServiceDisruption',
|
|
210
|
+
'SpeedManagement': 'sitSpeedManagement',
|
|
211
|
+
'TransitInformation': 'sitTransitInformation',
|
|
212
|
+
'VehicleObstruction': 'sitVehicleObstruction',
|
|
213
|
+
'WinterDrivingManagement': 'sitWinterDrivingManagement',
|
|
214
|
+
'Linear': 'locSingleRoadLinearLocation',
|
|
215
|
+
'NonOrderedLocationGroupByList': 'locLocationGroupByList',
|
|
216
|
+
'SituationPublication': 'sitSituationPublication',
|
|
217
|
+
'TpegNonJunctionPoint': 'locTpegNonJunctionPoint',
|
|
218
|
+
'AreaDestination': 'locAreaDestination',
|
|
219
|
+
'AlertCMethod4Linear': 'locAlertCMethod4Linear',
|
|
220
|
+
'Point': 'locPointLocation',
|
|
221
|
+
'Area': 'locAreaLocation',
|
|
222
|
+
'ItineraryByIndexedLocations': 'locItineraryByIndexedLocations',
|
|
223
|
+
'TpegGeometricArea': 'locTpegGeometricArea',
|
|
224
|
+
'TpegNamedOnlyArea': 'locTpegNamedOnlyArea',
|
|
225
|
+
'OpenlrPolygonLocationReference': 'locOpenlrPolygonLocationReference',
|
|
226
|
+
'DistanceFromLinearElementReferent': 'locDistanceFromLinearElementReferent',
|
|
227
|
+
# VMS
|
|
228
|
+
'VmsTablePublication': 'vmsVmsTablePublication',
|
|
229
|
+
'TpegSimplePoint': 'locTpegSimplePoint',
|
|
230
|
+
'AlertCMethod4Point': 'locAlertCMethod4Point',
|
|
231
|
+
# Parking
|
|
232
|
+
'ParkingStatusPublication': 'prkParkingStatusPublication',
|
|
233
|
+
'SpaceStatus': 'prkSpaceStatus',
|
|
234
|
+
'PlaceStatus': 'prkPlaceStatus',
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
def __init__(self):
|
|
238
|
+
pass
|
|
239
|
+
|
|
240
|
+
def xmlv2_to_json(self):
|
|
241
|
+
renamings = {
|
|
242
|
+
# xml2json artifact
|
|
243
|
+
'$': 'value',
|
|
244
|
+
# Assumed DATEXII 2.2->DATEX3.7 naming changes
|
|
245
|
+
'd2LogicalModel': 'payload',
|
|
246
|
+
'affectedCarriagewayAndLanes': 'carriageway',
|
|
247
|
+
'groupOfLocations': 'locationReference',
|
|
248
|
+
'locationForDisplay': 'coordinatesForDisplay',
|
|
249
|
+
'openlrCoordinate': 'openlrCoordinates',
|
|
250
|
+
'area': 'areaLocation',
|
|
251
|
+
'openlrExtendedLinear': 'openlrLinear',
|
|
252
|
+
'openlrLowestFRCToNextLRPoint': 'openlrLowestFrcToNextLRPoint',
|
|
253
|
+
'roadworksDuration': 'roadworksDurationClassification',
|
|
254
|
+
'roadsideServiceDisruptionType': 'serviceDisruptionType',
|
|
255
|
+
'carParkStatus': 'serviceDisruptionType',
|
|
256
|
+
# VMS
|
|
257
|
+
'vmsUnitTable': 'vmsControllerTable',
|
|
258
|
+
'vmsUnitRecord': 'vmsController',
|
|
259
|
+
'vmsUnitIdentifier': 'externalIdentifier',
|
|
260
|
+
'vmsUnitRecordExtension': 'vmsVmsControllerExtensionG',
|
|
261
|
+
'vmsRecord': 'vms',
|
|
262
|
+
'vmsRecordExtension': 'vmsVmsExtensionG',
|
|
263
|
+
'vmsPhysicalMounting': 'physicalSupport',
|
|
264
|
+
'vmsDescription': 'description',
|
|
265
|
+
'pictogramDisplayAreaIndex': 'displayAreaIndex',
|
|
266
|
+
'vmsUnitTableIdentification': 'vmsControllerTableIdentification',
|
|
267
|
+
'vmsPictogramDisplayCharacteristicsExtension': 'vmsPictogramDisplayAreaExtensionG',
|
|
268
|
+
#
|
|
269
|
+
'@modelBaseVersion': 'modelBaseVersion',
|
|
270
|
+
'@id': 'id',
|
|
271
|
+
'@index': 'index',
|
|
272
|
+
'@lang': 'lang',
|
|
273
|
+
'@version': 'version',
|
|
274
|
+
'@targetClass': 'targetClass',
|
|
275
|
+
'@xsi:type': 'type',
|
|
276
|
+
'@extensionName': 'extensionNameG',
|
|
277
|
+
'@extensionVersion': 'extensionVersionG',
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
def add_def_lang_if_missing(v):
|
|
281
|
+
return v if isinstance(v, dict) else {'lang': DEFAULT_LANGUAGE, 'value': v}
|
|
282
|
+
|
|
283
|
+
# TODO move value wrapping down to datex transform, here we should use them unwrapped
|
|
284
|
+
value_transforms = {
|
|
285
|
+
# type transformations
|
|
286
|
+
'offsetDistance': lambda v: int(v) if not isinstance(v, dict) else v,
|
|
287
|
+
# value transformations
|
|
288
|
+
# v3 removed some v2 enum values which need to be mapped
|
|
289
|
+
'commentType': lambda v: { # FIXME move value mapping down
|
|
290
|
+
'value': 'other' if v == 'locationDescriptor' else v
|
|
291
|
+
},
|
|
292
|
+
'vehicleObstructionType': lambda v: self.VEHICLE_OBSTRUCTION_TYPE_MAPPINGS.get(v, v),
|
|
293
|
+
'obstructionType': lambda v: self.OBSTRUCTION_TYPE_MAPPINGS.get(v, v),
|
|
294
|
+
'causeType': lambda v: self.CAUSE_TYPE_MAPPINGS.get(v, v),
|
|
295
|
+
'openlrFunctionalRoadClass': lambda v: {'value': v.lower()},
|
|
296
|
+
'openlrLowestFRCToNextLRPoint': lambda v: {'value': v.lower()},
|
|
297
|
+
# lang: de-DE -> de as JSON schema allows only 2 chars, bemas uses uppercase
|
|
298
|
+
'@lang': lambda v: v[0:2].lower(),
|
|
299
|
+
# comment/values is expected to be a dict with value/lang. For now, we assume values only includes multi ling string
|
|
300
|
+
'values': lambda v: [add_def_lang_if_missing(value) for value in v['value']],
|
|
301
|
+
'alertCDirection': lambda v: transform_alertc(v, False),
|
|
302
|
+
'alertCPoint': lambda v: [v] if isinstance(v, dict) else v,
|
|
303
|
+
'grossWeightCharacteristic': lambda v: v | {'typeOfWeight': {'value': 'actual'}},
|
|
304
|
+
# v3 has no carParkStatus any longer, we map to serviceDisruption restAreaClosed
|
|
305
|
+
'carParkStatus': lambda v: [{'value': self.CAR_PARK_STATUS_TO_SERVICE_TYPE_MAPPINGS.get(v, v)}],
|
|
306
|
+
'recurringDayWeekMonthPeriod': lambda v: {'comDayWeekMonth': v} if v is not None else None,
|
|
307
|
+
'supplementaryPositionalDescription': transform_supplementaryPositionalDescription,
|
|
308
|
+
'openlrPoiWithAccessPoint': wrap_lroffset,
|
|
309
|
+
'openlrPointAlongLine': wrap_lroffset,
|
|
310
|
+
'managedCause': lambda v: {'objectReference': v},
|
|
311
|
+
# VMS
|
|
312
|
+
'vmsType': lambda v: {'value': 'fullMatrixSign' if v == 'matrixSign' else v},
|
|
313
|
+
'vmsRecord': push_down_vms_configuration,
|
|
314
|
+
# 'roadMaintenanceType': lambda v: v if isinstance(v, list) else [wrap_with_value(v)], # TODO conversion issue?
|
|
315
|
+
'alertCLinear': lambda v: v if isinstance(v, list) else [v], # TODO conversion issue?
|
|
316
|
+
'pointAlongLinearElement': lambda v: v if isinstance(v, list) else [v],
|
|
317
|
+
'linearWithinLinearElement': lambda v: v if isinstance(v, list) else [v],
|
|
318
|
+
'linearElement': lambda v: {'locLinearElement': v},
|
|
319
|
+
'gmlLineString': lambda v: {
|
|
320
|
+
'posList': v.get('posList', v.get('coordinates', '').replace(',', ' ')),
|
|
321
|
+
'srsName': v.get('srsName'),
|
|
322
|
+
},
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
keys_to_drop = {
|
|
326
|
+
'urgency': [], # Apparently, support for urgency has been dropped?
|
|
327
|
+
'@xmlns:xsi': [],
|
|
328
|
+
'@version': ['situation'],
|
|
329
|
+
'type': ['recurringTimePeriodOfDay', 'cause'],
|
|
330
|
+
'management': ['situationRecord'], # apparently support for lifecyclemanagement notifications has been dropped?
|
|
331
|
+
'openlrGeoCoordinate': ['openlrPointLocationReference'],
|
|
332
|
+
'@xmlns': [],
|
|
333
|
+
'exchange': [],
|
|
334
|
+
'originalNumberOfLanes': ['impact'], # TODO, check, if we shold copy this value to carriageways
|
|
335
|
+
# v3 dropped CarParks Situation. We map to serviceDisruption which seems not to have an equivalent to carParkIdentity:
|
|
336
|
+
'carParkIdentity': [],
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
geojson_drops = {'situationExtension': []}
|
|
340
|
+
|
|
341
|
+
# keys_to_pull_up convert { ..., '<value>': { <nested properties> } } in { ..., <nested properties>}
|
|
342
|
+
keys_to_pull_up = {
|
|
343
|
+
'linearExtension': None,
|
|
344
|
+
'linearExtended': None,
|
|
345
|
+
'linearLineStringExtension': None,
|
|
346
|
+
'pointExtension': None,
|
|
347
|
+
'openlrExtendedPoint': None,
|
|
348
|
+
'areaExtension': None,
|
|
349
|
+
'openlrExtendedArea': None,
|
|
350
|
+
'payloadPublication': None,
|
|
351
|
+
'locGmlLineString': None,
|
|
352
|
+
'impact': ['trafficConstrictionType'],
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return TransformationRules(
|
|
356
|
+
renamings,
|
|
357
|
+
value_transforms,
|
|
358
|
+
keys_to_drop | geojson_drops,
|
|
359
|
+
{},
|
|
360
|
+
keys_to_pull_up,
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
def xmlv3_to_json3_7(self):
|
|
364
|
+
datex_json_renamings = {
|
|
365
|
+
# Assumed DATEXII JSON Schema Artifacts
|
|
366
|
+
'@modelBaseVersion': 'modelBaseVersionG',
|
|
367
|
+
'@id': 'idG',
|
|
368
|
+
'@lang': 'lang',
|
|
369
|
+
'@version': 'versionG',
|
|
370
|
+
'@targetClass': 'targetClass',
|
|
371
|
+
'@operatingPatternIndex': 'operatingPatternIndex',
|
|
372
|
+
'@extensionName': 'extensionNameG',
|
|
373
|
+
'@extensionVersion': 'extensionVersionG',
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
def wrap_with_value(v):
|
|
377
|
+
return {'value': v}
|
|
378
|
+
|
|
379
|
+
value_transforms = {
|
|
380
|
+
# Value wrapping for parking ...
|
|
381
|
+
'status': wrap_with_value,
|
|
382
|
+
'operationStatus': wrap_with_value,
|
|
383
|
+
'informationStatus': wrap_with_value,
|
|
384
|
+
'confidentiality': wrap_with_value,
|
|
385
|
+
}
|
|
386
|
+
keys_to_drop = {
|
|
387
|
+
'@xsi:schemaLocation': [],
|
|
388
|
+
'@xmlns': [],
|
|
389
|
+
'@xmlns:d2': [],
|
|
390
|
+
'@xmlns:loc': [],
|
|
391
|
+
'@xmlns:com': [],
|
|
392
|
+
'@xmlns:fac': [],
|
|
393
|
+
'@xmlns:xsd': [],
|
|
394
|
+
'@xmlns:xsi': [],
|
|
395
|
+
'@xsi:type': [],
|
|
396
|
+
}
|
|
397
|
+
keys_to_pull_up = {'modelBaseVersion': None}
|
|
398
|
+
return TransformationRules(
|
|
399
|
+
datex_json_renamings,
|
|
400
|
+
value_transforms,
|
|
401
|
+
keys_to_drop,
|
|
402
|
+
self.datex_xml_2_json_xsitype_mappings,
|
|
403
|
+
keys_to_pull_up,
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
def xmlv2_to_json3_7(self):
|
|
407
|
+
datex_json_renamings = {
|
|
408
|
+
# Assumed DATEXII JSON Schema Artifacts
|
|
409
|
+
'@modelBaseVersion': 'modelBaseVersionG',
|
|
410
|
+
'@id': 'idG',
|
|
411
|
+
'@lang': 'lang',
|
|
412
|
+
'@version': 'versionG',
|
|
413
|
+
'@targetClass': 'targetClass',
|
|
414
|
+
# XML to JSON-schema conversion artefacts which were introduced by DATEX to avoid name clashes
|
|
415
|
+
'commentExtension': 'sitCommentExtensionG',
|
|
416
|
+
'headerInformationExtension': 'comHeaderInformationExtensionG',
|
|
417
|
+
'generalObstructionExtension': 'sitGeneralObstructionExtensionG',
|
|
418
|
+
'impactExtension': 'sitImpactExtensionG',
|
|
419
|
+
'operatorActionExtension': 'sitOperatorActionExtensionG',
|
|
420
|
+
'roadOrCarriagewayOrLaneManagementExtension': 'sitRoadOrCarriagewayOrLaneManagementExtensionG',
|
|
421
|
+
'roadworksExtension': 'sitRoadworksExtensionG',
|
|
422
|
+
'situationExtension': 'sitSituationExtensionG',
|
|
423
|
+
'situationRecordExtension': 'sitSituationRecordExtensionG',
|
|
424
|
+
'payloadPublicationExtension': 'comPayloadPublicationExtensionG',
|
|
425
|
+
'affectedCarriagewayAndLanesExtension': 'locCarriagewayExtensionG',
|
|
426
|
+
# Note: the following extendedLinear extension is not defined in schema, JSON might not match expectation
|
|
427
|
+
'extendedLinear': 'locLinearLocationExtensionG',
|
|
428
|
+
# Note: the following networkLocationExtension extension is not defined in schema, JSON might not match expectation
|
|
429
|
+
'networkLocationExtension': 'locNetworkLocationExtensionG',
|
|
430
|
+
'openlrPointAlongLine': 'locOpenlrPointAlongLine',
|
|
431
|
+
'openlrPoiWithAccessPoint': 'locOpenlrPoiWithAccessPoint',
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
# A couple of text properties are wrapped in an object, presumably for extendability
|
|
435
|
+
def wrap_with_value(v):
|
|
436
|
+
return {'value': v}
|
|
437
|
+
|
|
438
|
+
value_transforms = {
|
|
439
|
+
# Value wrapping...
|
|
440
|
+
'abnormalTrafficType': wrap_with_value,
|
|
441
|
+
'accidentType': lambda v: {'value': self.ACCIDENT_TYPE_MAPPINGS.get(v, v)},
|
|
442
|
+
'informationStatus': wrap_with_value,
|
|
443
|
+
'confidentiality': wrap_with_value,
|
|
444
|
+
'applicableForTrafficDirection': wrap_with_value,
|
|
445
|
+
'constructionWorkType': wrap_with_value,
|
|
446
|
+
'delaysType': wrap_with_value,
|
|
447
|
+
'probabilityOfOccurrence': wrap_with_value,
|
|
448
|
+
'poorEnvironmentType': wrap_with_value,
|
|
449
|
+
'roadMaintenanceType': wrap_with_value,
|
|
450
|
+
'mobilityType': wrap_with_value,
|
|
451
|
+
'locationDescription': wrap_with_value,
|
|
452
|
+
'operatorActionStatus': wrap_with_value,
|
|
453
|
+
'tpegDirection': wrap_with_value,
|
|
454
|
+
'tpegLinearLocationType': wrap_with_value,
|
|
455
|
+
'tpegOtherPointDescriptorType': wrap_with_value,
|
|
456
|
+
'trafficManuallyDirectedBy': wrap_with_value,
|
|
457
|
+
'validityStatus': wrap_with_value,
|
|
458
|
+
'alertCDirection': lambda v: transform_alertc(v, True),
|
|
459
|
+
'openlrFormOfWay': wrap_with_value,
|
|
460
|
+
'complianceOption': wrap_with_value,
|
|
461
|
+
'placesAtWhichApplicable': wrap_with_value,
|
|
462
|
+
'loadType': wrap_with_value,
|
|
463
|
+
'applicableForTrafficType': wrap_with_value,
|
|
464
|
+
'roadOrCarriagewayOrLaneManagementType': wrap_with_value,
|
|
465
|
+
'disturbanceActivityType': wrap_with_value,
|
|
466
|
+
'vehicleType': wrap_with_value,
|
|
467
|
+
'referentType': wrap_with_value,
|
|
468
|
+
'directionRelativeOnLinearSection': wrap_with_value,
|
|
469
|
+
'comparisonOperator': wrap_with_value,
|
|
470
|
+
'generalInstructionToRoadUsersType': wrap_with_value,
|
|
471
|
+
'environmentalObstructionType': wrap_with_value,
|
|
472
|
+
'roadworksDuration': wrap_with_value,
|
|
473
|
+
'drivingConditionType': wrap_with_value,
|
|
474
|
+
'openlrOrientation': wrap_with_value,
|
|
475
|
+
'openlrSideOfRoad': wrap_with_value,
|
|
476
|
+
'tpegAreaLocationType': wrap_with_value,
|
|
477
|
+
'subjectTypeOfWorks': wrap_with_value,
|
|
478
|
+
'applicableDay': wrap_with_value,
|
|
479
|
+
'reroutingManagementType': wrap_with_value,
|
|
480
|
+
'animalPresenceType': wrap_with_value,
|
|
481
|
+
'severity': wrap_with_value,
|
|
482
|
+
'authorityOperationType': wrap_with_value,
|
|
483
|
+
'equipmentOrSystemFaultType': wrap_with_value,
|
|
484
|
+
'faultyEquipmentOrSystemType': wrap_with_value,
|
|
485
|
+
'generalNetworkManagementType': wrap_with_value,
|
|
486
|
+
'overallSeverity': wrap_with_value,
|
|
487
|
+
'carriageway': wrap_with_value,
|
|
488
|
+
'infrastructureDamageType': wrap_with_value,
|
|
489
|
+
'speedManagementType': wrap_with_value,
|
|
490
|
+
'publicEventType': wrap_with_value,
|
|
491
|
+
'weatherRelatedRoadConditionType': wrap_with_value,
|
|
492
|
+
'nonWeatherRelatedRoadConditionType': wrap_with_value,
|
|
493
|
+
'maintenanceVehicleActions': wrap_with_value,
|
|
494
|
+
'roadsideServiceDisruptionType': wrap_with_value,
|
|
495
|
+
'trafficConstrictionType': wrap_with_value,
|
|
496
|
+
'tpegAreaDescriptorType': wrap_with_value,
|
|
497
|
+
'transitServiceInformation': wrap_with_value,
|
|
498
|
+
'transitServiceType': wrap_with_value,
|
|
499
|
+
'directionRelativeAtPoint': wrap_with_value,
|
|
500
|
+
'vehicleObstructionType': lambda v: {'value': self.VEHICLE_OBSTRUCTION_TYPE_MAPPINGS.get(v, v)},
|
|
501
|
+
# see https://docs.ndw.nu/en/dataformaten/datex2-v2.3/conversie-naar-v3/#generalobstruction-maps-to-generalobstruction
|
|
502
|
+
'obstructionType': lambda v: {'value': self.OBSTRUCTION_TYPE_MAPPINGS.get(v, v)},
|
|
503
|
+
'causeType': lambda v: {'value': self.CAUSE_TYPE_MAPPINGS.get(v, v)},
|
|
504
|
+
# vms
|
|
505
|
+
'physicalSupport': wrap_with_value,
|
|
506
|
+
'vmsPhysicalMounting': wrap_with_value,
|
|
507
|
+
'tpegSimplePointLocationType': wrap_with_value,
|
|
508
|
+
'alertCArea': lambda v: [v], # FIXME move up to xml2_to_json?
|
|
509
|
+
'gmlLineString': lambda v: {'locGmlLineString': {'posList': v.get('posList', v.get('coordinates', '').replace(',', ' '))}},
|
|
510
|
+
'lane': lambda v: {'laneNumber': int(v[4:])} if v.startswith('lane') else {'laneUsage': {'value': v}},
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
keys_to_drop = {
|
|
514
|
+
'@xsi:schemaLocation': [],
|
|
515
|
+
'@xmlns:xsd': [],
|
|
516
|
+
'@xsi:type': [],
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
rules = self.xmlv2_to_json()
|
|
520
|
+
return TransformationRules(
|
|
521
|
+
rules.renamings | datex_json_renamings,
|
|
522
|
+
rules.value_transforms | value_transforms,
|
|
523
|
+
rules.keys_to_drop | keys_to_drop,
|
|
524
|
+
self.datex_xml_2_json_xsitype_mappings,
|
|
525
|
+
rules.keys_to_pull_up,
|
|
526
|
+
)
|