diff --git a/pom.xml b/pom.xml index a17212f4..af9a5831 100644 --- a/pom.xml +++ b/pom.xml @@ -22,22 +22,27 @@ fi.fmi.avi.converter fmi-avi-messageconverter - 7.0.0-beta1 + 7.0.0-beta2-SNAPSHOT fi.fmi.xmlmodel fmi-avi-xmlmodel-iwxxm211 - 1.5.0 + 2.0.0 fi.fmi.xmlmodel fmi-avi-xmlmodel-iwxxm30 - 1.5.0 + 2.0.0 + + + fi.fmi.xmlmodel + fmi-avi-xmlmodel-iwxxm2023-1 + 2.0.0 fi.fmi.xmlmodel fmi-avi-xmlmodel-wmo-collect2014 - 1.5.0 + 2.0.0 net.sf.saxon diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511FullSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511FullSerializer.java new file mode 100644 index 00000000..85d0f127 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511FullSerializer.java @@ -0,0 +1,143 @@ +package fi.fmi.avi.converter.iwxxm; + +import aero.aixm511full.SurfacePropertyType; +import aero.aixm511full.SurfaceType; +import aero.aixm511full.ValDistanceVerticalType; +import fi.fmi.avi.converter.ConversionException; +import fi.fmi.avi.converter.ConversionHints; +import fi.fmi.avi.model.*; +import fi.fmi.avi.model.immutable.NumericMeasureImpl; +import net.opengis.gml32.*; +import org.w3c.dom.Document; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.StringWriter; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.UUID; + +/** + * Common functionality for serializing aviation messages into IWXXM. Uses the full AIXM 5.1.1 schema. + */ +public abstract class AbstractIWXXMAixm511FullSerializer extends AbstractIWXXMSerializer { + + private static JAXBContext aixm511FullJaxbContext = null; + + /** + * Singleton for accessing the shared JAXBContext for IWXXM JAXB handling. Uses the full AIXM 5.1.1 schema. + *

+ * NOTE: this can take several seconds when done for the first time after JVM start, + * needs to scan all the jars in classpath. + * + * @return the context + * @throws JAXBException if the context cannot be created + */ + public static synchronized JAXBContext getAixm511FullJAXBContext() throws JAXBException { + if (aixm511FullJaxbContext == null) { + aixm511FullJaxbContext = JAXBContext.newInstance("icao.iwxxm2023_1:aero.aixm511full:net.opengis.gml32:org.iso19139.ogc2007.gmd:org.iso19139.ogc2007.gco:org" + + ".iso19139.ogc2007.gss:org.iso19139.ogc2007.gts:org.iso19139.ogc2007.gsr:net.opengis.om20:net.opengis.sampling:net.opengis.sampling" + + ".spatial:wmo.metce2013:wmo.opm2013:wmo.collect2014:org.w3c.xlink11"); + } + return aixm511FullJaxbContext; + } + + protected Document renderXMLDocument(final Object input, final ConversionHints hints) throws ConversionException { + final StringWriter sw = new StringWriter(); + try { + final Marshaller marshaller = getAixm511FullJAXBContext().createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, getSchemaInfo().getCombinedSchemaLocations()); + marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", getNamespaceContext()); + marshaller.marshal(wrap(input, (Class) input.getClass()), sw); + return asCleanedUpXML(sw.toString(), hints); + } catch (final JAXBException e) { + throw new ConversionException("Exception in rendering to DOM", e); + } + } + + protected static SurfacePropertyType createAixm511fullSurface(final Geometry geom, final String id) throws IllegalArgumentException { + SurfacePropertyType retval = null; + if (geom != null) { + retval = create(SurfacePropertyType.class, spt -> spt.setSurface(createAndWrap(SurfaceType.class, sft -> { + geom.getCrs().ifPresent(crs -> setCrsToType(sft, crs)); + sft.setId(id); + final JAXBElement spapt; + if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass()) || PointGeometry.class.isAssignableFrom(geom.getClass())) { + final List centerPointCoords; + final LengthType radius; + if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass())) { + final CircleByCenterPoint cbcp = (CircleByCenterPoint) geom; + centerPointCoords = cbcp.getCenterPointCoordinates(); + radius = asMeasure(cbcp.getRadius(), LengthType.class); + } else { + //Create a zero-radius circle if a point geometry is given + final PointGeometry point = (PointGeometry) geom; + centerPointCoords = point.getCoordinates(); + radius = asMeasure(NumericMeasureImpl.of(0.0, "[nmi_i]"), LengthType.class); + } + + final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior( + create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(RingType.class, rt -> rt.getCurveMember() + .add(create(CurvePropertyType.class, curvept -> curvept.setAbstractCurve(createAndWrap(CurveType.class, curvet -> { + curvet.setId(UUID_PREFIX + UUID.randomUUID()); + curvet.setSegments(create(CurveSegmentArrayPropertyType.class, + curvesat -> curvesat.getAbstractCurveSegment().add(createAndWrap(CircleByCenterPointType.class, cbcpt -> { + cbcpt.setPos(create(DirectPositionType.class, dpt -> dpt.getValue().addAll(centerPointCoords))); + cbcpt.setNumArc(BigInteger.valueOf(1)); + cbcpt.setRadius(radius); + })))); + }))))))))); + spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); + } else if (PolygonGeometry.class.isAssignableFrom(geom.getClass())) { //Polygon + final PolygonGeometry polygon = (PolygonGeometry) geom; + final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior( + create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(LinearRingType.class, lrt -> { + final DirectPositionListType dplt = create(DirectPositionListType.class, + dpl -> dpl.getValue().addAll(polygon.getExteriorRingPositions(Winding.COUNTERCLOCKWISE))); + lrt.setPosList(dplt); + }))))); + spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); + } else { + throw new IllegalArgumentException("Unable to create a Surface from geometry of type " + geom.getClass().getCanonicalName()); + } + if (spapt != null) { + sft.setPatches(spapt); + } + }))); + } + return retval; + } + + protected static Optional toValDistanceVertical(final NumericMeasure numericMeasure) { + return numericMeasure == null ? Optional.empty() : valDistanceVertical(numericMeasure.getValue(), numericMeasure.getUom()); + } + + protected static Optional valDistanceVertical(final Double value, final String uom) { + return valDistanceVertical(value == null ? Double.NaN : value, uom); + } + + protected static Optional valDistanceVertical(final double value, final String uom) { + if (Double.isNaN(value)) { + return Optional.empty(); + } + final ValDistanceVerticalType type = create(ValDistanceVerticalType.class); + final DecimalFormat format = new DecimalFormat("", DecimalFormatSymbols.getInstance(Locale.US)); + format.setMinimumIntegerDigits(1); + format.setMinimumFractionDigits(0); + format.setMaximumFractionDigits(4); + format.setGroupingUsed(false); + type.setValue(format.format(value)); + if (uom != null && !uom.isEmpty()) { + type.setUom(uom.toUpperCase(Locale.US)); + } + return Optional.of(type); + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxParser.java b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxParser.java new file mode 100644 index 00000000..9bf41cf3 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxParser.java @@ -0,0 +1,88 @@ +package fi.fmi.avi.converter.iwxxm; + +import fi.fmi.avi.converter.ConversionException; +import fi.fmi.avi.converter.ConversionHints; +import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.ConversionResult; +import fi.fmi.avi.model.AviationWeatherMessageOrCollection; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.xml.sax.SAXException; + +import javax.xml.bind.Binder; +import javax.xml.bind.JAXBException; +import javax.xml.bind.ValidationEvent; +import java.util.List; + +/** + * Created by rinne on 25/07/2018. + */ +public abstract class AbstractIWXXMAixm511WxParser extends AbstractIWXXMParser { + + /** + * Converts a message into a POJO. + *

+ * The IWXXM TAF message parsing is done in two phases: + * <ul> + * <li>In the first phase the IWXXM DOM document is validated against its + * XML Schema and Schematron rules and (if validation passed), the JAXB objects created from the + * DOM scanned for all the necessary property values for creating MessageConverter Java model objects. Additional validation + * for the document structure and content is also done in this phase.</li> + * <li>In the second phase the model objects are created and populated from the property data + * collected in the first phase.</li> + * </ul> + * + * @param input input message + * @param hints parsing hints + * @return the conversion result + */ + @Override + public ConversionResult convertMessage(final T input, final ConversionHints hints) { + final ConversionResult result = new ConversionResult<>(); + final Object source; + final ReferredObjectRetrievalContext refCtx; + + try { + final Document dom = parseAsDom(input); + final XMLSchemaInfo schemaInfo = getSchemaInfo(); + final Binder binder = AbstractIWXXMAixm511WxSerializer.getAixm511WxJAXBContext().createBinder(); + + //XML Schema validation upon JAXB unmarshal: + binder.setSchema(schemaInfo.getSchema()); + final IWXXMValidationEventHandler collector = new IWXXMValidationEventHandler(); + binder.setEventHandler(collector); + source = binder.unmarshal(dom); + + final List events = collector.getEvents(); + if (events.isEmpty()) { + //Reset binder event handler after validation: + binder.setEventHandler(null); + + refCtx = new ReferredObjectRetrievalContext(dom, binder); + + //Schematron validation: + result.addIssue(validateAgainstIWXXMSchematron(dom, schemaInfo, hints)); + try { + result.setConvertedMessage(createPOJO(source, refCtx, result, hints)); + } catch (final IllegalStateException ise) { + result.addIssue(new ConversionIssue(ConversionIssue.Severity.ERROR, ConversionIssue.Type.MISSING_DATA, + "All mandatory information for " + "constructing a message object was not available", ise)); + } + } else { + for (final ValidationEvent evt : collector.getEvents()) { + result.addIssue( + new ConversionIssue(ConversionIssue.Type.SYNTAX, "XML Schema validation issue: " + evt.getMessage(), evt.getLinkedException())); + } + } + + } catch (final ConversionException ce) { + result.addIssue(new ConversionIssue(ConversionIssue.Type.SYNTAX, "Unable to parse input as an XML document", ce)); + return result; + } catch (JAXBException | SAXException e) { + throw new RuntimeException("Unexpected exception in parsing IWXXM content", e); + } + + return result; + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxSerializer.java new file mode 100644 index 00000000..f83cec4d --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMAixm511WxSerializer.java @@ -0,0 +1,181 @@ +package fi.fmi.avi.converter.iwxxm; + +import aero.aixm511.SurfacePropertyType; +import aero.aixm511.SurfaceType; +import aero.aixm511.*; +import fi.fmi.avi.converter.ConversionException; +import fi.fmi.avi.converter.ConversionHints; +import fi.fmi.avi.model.*; +import fi.fmi.avi.model.immutable.NumericMeasureImpl; +import net.opengis.gml32.CurvePropertyType; +import net.opengis.gml32.CurveType; +import net.opengis.gml32.*; +import org.w3c.dom.Document; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import java.io.StringWriter; +import java.math.BigInteger; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.List; +import java.util.Locale; +import java.util.Optional; +import java.util.UUID; + +/** + * Common functionality for serializing aviation messages into IWXXM. Uses the AIXM 5.1.1 WX schema. + */ +public abstract class AbstractIWXXMAixm511WxSerializer extends AbstractIWXXMSerializer { + + private static JAXBContext aixm511WxJaxbContext = null; + + /** + * Singleton for accessing the shared JAXBContext for IWXXM JAXB handling. Uses the AIXM 5.1.1 WX schema. + *

+ * NOTE: this can take several seconds when done for the first time after JVM start, + * needs to scan all the jars in classpath. + * + * @return the context + * @throws JAXBException if the context cannot be created + */ + public static synchronized JAXBContext getAixm511WxJAXBContext() throws JAXBException { + if (aixm511WxJaxbContext == null) { + aixm511WxJaxbContext = JAXBContext.newInstance("icao.iwxxm21:icao.iwxxm30:aero.aixm511:net.opengis.gml32:org.iso19139.ogc2007.gmd:org.iso19139.ogc2007.gco:org" + + ".iso19139.ogc2007.gss:org.iso19139.ogc2007.gts:org.iso19139.ogc2007.gsr:net.opengis.om20:net.opengis.sampling:net.opengis.sampling" + + ".spatial:wmo.metce2013:wmo.opm2013:wmo.collect2014:org.w3c.xlink11"); + } + return aixm511WxJaxbContext; + } + + protected Document renderXMLDocument(final Object input, final ConversionHints hints) throws ConversionException { + final StringWriter sw = new StringWriter(); + try { + final Marshaller marshaller = getAixm511WxJAXBContext().createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); + marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, getSchemaInfo().getCombinedSchemaLocations()); + marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", getNamespaceContext()); + marshaller.marshal(wrap(input, (Class) input.getClass()), sw); + return asCleanedUpXML(sw.toString(), hints); + } catch (final JAXBException e) { + throw new ConversionException("Exception in rendering to DOM", e); + } + } + + protected static SurfacePropertyType createSurface(final Geometry geom, final String id) throws IllegalArgumentException { + SurfacePropertyType retval = null; + if (geom != null) { + retval = create(SurfacePropertyType.class, spt -> spt.setSurface(createAndWrap(SurfaceType.class, sft -> { + geom.getCrs().ifPresent(crs -> setCrsToType(sft, crs)); + sft.setId(id); + final JAXBElement spapt; + if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass()) || PointGeometry.class.isAssignableFrom(geom.getClass())) { + final List centerPointCoords; + final LengthType radius; + if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass())) { + final CircleByCenterPoint cbcp = (CircleByCenterPoint) geom; + centerPointCoords = cbcp.getCenterPointCoordinates(); + radius = asMeasure(cbcp.getRadius(), LengthType.class); + } else { + //Create a zero-radius circle if a point geometry is given + final PointGeometry point = (PointGeometry) geom; + centerPointCoords = point.getCoordinates(); + radius = asMeasure(NumericMeasureImpl.of(0.0, "[nmi_i]"), LengthType.class); + } + + final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior(create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(RingType.class, rt -> rt.getCurveMember().add(create(CurvePropertyType.class, curvept -> curvept.setAbstractCurve(createAndWrap(CurveType.class, curvet -> { + curvet.setId(UUID_PREFIX + UUID.randomUUID()); + curvet.setSegments(create(CurveSegmentArrayPropertyType.class, curvesat -> curvesat.getAbstractCurveSegment().add(createAndWrap(CircleByCenterPointType.class, cbcpt -> { + cbcpt.setPos(create(DirectPositionType.class, dpt -> dpt.getValue().addAll(centerPointCoords))); + cbcpt.setNumArc(BigInteger.valueOf(1)); + cbcpt.setRadius(radius); + })))); + }))))))))); + spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); + } else if (PolygonGeometry.class.isAssignableFrom(geom.getClass())) { //Polygon + final PolygonGeometry polygon = (PolygonGeometry) geom; + final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior(create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(LinearRingType.class, lrt -> { + final DirectPositionListType dplt = create(DirectPositionListType.class, dpl -> dpl.getValue().addAll(polygon.getExteriorRingPositions(Winding.COUNTERCLOCKWISE))); + lrt.setPosList(dplt); + }))))); + spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); + } else { + throw new IllegalArgumentException("Unable to create a Surface from geometry of type " + geom.getClass().getCanonicalName()); + } + if (spapt != null) { + sft.setPatches(spapt); + } + }))); + } + return retval; + } + + protected static Optional elevationToValDistanceVertical(final ElevatedPoint elevatedPoint) { + return elevatedPoint == null ? Optional.empty() : valDistanceVertical(elevatedPoint.getElevationValue().orElse(Double.NaN), elevatedPoint.getElevationUom().orElse("")); + } + + protected static Optional toValDistanceVertical(final NumericMeasure numericMeasure) { + return numericMeasure == null ? Optional.empty() : valDistanceVertical(numericMeasure.getValue(), numericMeasure.getUom()); + } + + protected static Optional valDistanceVertical(final Double value, final String uom) { + return valDistanceVertical(value == null ? Double.NaN : value, uom); + } + + protected static Optional valDistanceVertical(final double value, final String uom) { + if (Double.isNaN(value)) { + return Optional.empty(); + } + final ValDistanceVerticalType type = create(ValDistanceVerticalType.class); + final DecimalFormat format = new DecimalFormat("", DecimalFormatSymbols.getInstance(Locale.US)); + format.setMinimumIntegerDigits(1); + format.setMinimumFractionDigits(0); + format.setMaximumFractionDigits(4); + format.setGroupingUsed(false); + type.setValue(format.format(value)); + if (uom != null && !uom.isEmpty()) { + type.setUom(uom.toUpperCase(Locale.US)); + } + return Optional.of(type); + } + + protected static ValDistanceVerticalType nilValDistanceVertical() { + final ValDistanceVerticalType type = create(ValDistanceVerticalType.class); + // TODO: how to set xsi:nil="true"? + type.setNilReason("unknown"); + type.setUom("OTHER"); + return type; + } + + protected void setAerodromeData(final AirportHeliportType aerodrome, final Aerodrome input, final String aerodromeId, final String timeSliceIdPrefix, final String elevatedPointIdPrefix) { + if (input == null) { + return; + } + aerodrome.setId(aerodromeId); + aerodrome.getTimeSlice().add(create(AirportHeliportTimeSlicePropertyType.class, prop -> prop.setAirportHeliportTimeSlice(create(AirportHeliportTimeSliceType.class, timeSlice -> { + timeSlice.setId(timeSliceIdPrefix + UUID.randomUUID()); + timeSlice.setValidTime(create(TimePrimitivePropertyType.class)); + timeSlice.setInterpretation("SNAPSHOT"); + timeSlice.setDesignator(create(CodeAirportHeliportDesignatorType.class, designator -> designator.setValue(input.getDesignator()))); + input.getName().ifPresent(inputName -> timeSlice.setPortName(create(TextNameType.class, name -> name.setValue(inputName.toUpperCase(Locale.US))))); + input.getLocationIndicatorICAO().ifPresent(inputLocator -> timeSlice.setLocationIndicatorICAO(create(CodeICAOType.class, locator -> locator.setValue(inputLocator)))); + + input.getDesignatorIATA().ifPresent(inputDesignator -> timeSlice.setDesignatorIATA(create(CodeIATAType.class, designator -> designator.setValue(inputDesignator)))); + valDistanceVertical(input.getFieldElevationValue().orElse(Double.NaN), input.getFieldElevationUom().orElse(""))// + .ifPresent(timeSlice::setFieldElevation); + + input.getReferencePoint().ifPresent(inputPosition -> timeSlice.setARP(create(ElevatedPointPropertyType.class, elevatedPointProp -> elevatedPointProp.setElevatedPoint(create(ElevatedPointType.class, point -> { + point.setId(elevatedPointIdPrefix + UUID.randomUUID()); + inputPosition.getCrs().ifPresent(crs -> setCrsToType(point, crs)); + if (inputPosition.getCoordinates() != null) { + point.setPos(create(DirectPositionType.class, pos -> pos.getValue().addAll(inputPosition.getCoordinates()))); + } + elevationToValDistanceVertical(inputPosition).ifPresent(point::setElevation); + inputPosition.getVerticalDatum().ifPresent(verticalDatum -> point.setVerticalDatum(create(CodeVerticalDatumType.class, verticalCode -> verticalCode.setValue(verticalDatum)))); + }))))); + })))); + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMParser.java b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMParser.java index 1152ed20..35412dd3 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMParser.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMParser.java @@ -1,23 +1,16 @@ package fi.fmi.avi.converter.iwxxm; -import java.util.ArrayList; -import java.util.List; - -import javax.xml.bind.Binder; -import javax.xml.bind.JAXBException; -import javax.xml.bind.ValidationEvent; -import javax.xml.bind.ValidationEventHandler; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.xml.sax.SAXException; - import fi.fmi.avi.converter.AviMessageSpecificConverter; import fi.fmi.avi.converter.ConversionException; import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; import fi.fmi.avi.converter.ConversionResult; import fi.fmi.avi.model.AviationWeatherMessageOrCollection; +import org.w3c.dom.Document; + +import javax.xml.bind.ValidationEvent; +import javax.xml.bind.ValidationEventHandler; +import java.util.ArrayList; +import java.util.List; /** * Created by rinne on 25/07/2018. @@ -43,77 +36,7 @@ protected abstract S createPOJO(final Object source, final ReferredObjectRetriev protected abstract XMLSchemaInfo getSchemaInfo(); - /** - * Converts a message into a POJO. - * - * The IWXXM TAF message parsing is done in two phases: - * <ul> - * <li>In the first phase the IWXXM DOM document is validated against the it's - * XML Schema and Schematron rules and (if validation passed), the JAXB objects created from the the - * DOM scanned for all the necessary property values for creating MessageConverter Java model objects. Additional validation - * for the document structure and content is also done in this phase.</li> - * <li>In the second phase the model objects are created and populated from the property data - * collected in the first phase.</li> - * </ul> - * - * @param input - * input message - * @param hints - * parsing hints - * - * @return the conversion result - */ - @Override - public ConversionResult convertMessage(final T input, final ConversionHints hints) { - final ConversionResult result = new ConversionResult<>(); - final Object source; - final ReferredObjectRetrievalContext refCtx; - - try { - final Document dom = parseAsDom(input); - final XMLSchemaInfo schemaInfo = getSchemaInfo(); - final Binder binder = getJAXBContext().createBinder(); - - //XML Schema validation upon JAXB unmarshal: - binder.setSchema(schemaInfo.getSchema()); - final IWXXMValidationEventHandler collector = new IWXXMValidationEventHandler(); - binder.setEventHandler(collector); - source = binder.unmarshal(dom); - - final List events = collector.getEvents(); - if (events.isEmpty()) { - //Reset binder event handler after validation: - binder.setEventHandler(null); - - refCtx = new ReferredObjectRetrievalContext(dom, binder); - - //Schematron validation: - result.addIssue(validateAgainstIWXXMSchematron(dom, schemaInfo, hints)); - try { - result.setConvertedMessage(createPOJO(source, refCtx, result, hints)); - } catch (final IllegalStateException ise) { - result.addIssue(new ConversionIssue(ConversionIssue.Severity.ERROR, ConversionIssue.Type.MISSING_DATA, - "All mandatory information for " + "constructing a message object was not available", ise)); - } - } else { - for (final ValidationEvent evt : collector.getEvents()) { - result.addIssue( - new ConversionIssue(ConversionIssue.Type.SYNTAX, "XML Schema validation issue: " + evt.getMessage(), evt.getLinkedException())); - } - } - - } catch (final ConversionException ce) { - result.addIssue(new ConversionIssue(ConversionIssue.Type.SYNTAX, "Unable to parse input as an XML document", ce)); - return result; - } catch (JAXBException | SAXException e) { - throw new RuntimeException("Unexpected exception in parsing IWXXM content", e); - } - - return result; - - } - - private static class IWXXMValidationEventHandler implements ValidationEventHandler { + public static class IWXXMValidationEventHandler implements ValidationEventHandler { private final List events = new ArrayList<>(); @@ -123,7 +46,7 @@ public boolean handleEvent(final ValidationEvent event) { return true; } - List getEvents() { + public List getEvents() { return events; } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMSerializer.java index 605cec8b..c58d6cad 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/AbstractIWXXMSerializer.java @@ -1,23 +1,14 @@ package fi.fmi.avi.converter.iwxxm; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.math.BigInteger; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Locale; -import java.util.Optional; -import java.util.UUID; +import fi.fmi.avi.converter.*; +import fi.fmi.avi.model.*; +import fi.fmi.avi.model.taf.TAF; +import net.opengis.gml32.*; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import javax.xml.XMLConstants; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -26,127 +17,19 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; - - -import net.opengis.gml32.AbstractGeometryType; -import net.opengis.gml32.AbstractRingPropertyType; -import net.opengis.gml32.CircleByCenterPointType; -import net.opengis.gml32.CurvePropertyType; -import net.opengis.gml32.CurveSegmentArrayPropertyType; -import net.opengis.gml32.CurveType; -import net.opengis.gml32.DirectPositionListType; -import net.opengis.gml32.DirectPositionType; -import net.opengis.gml32.LengthType; -import net.opengis.gml32.LinearRingType; -import net.opengis.gml32.MeasureType; -import net.opengis.gml32.PolygonPatchType; -import net.opengis.gml32.RingType; -import net.opengis.gml32.SurfacePatchArrayPropertyType; -import net.opengis.gml32.TimeInstantPropertyType; -import net.opengis.gml32.TimeInstantType; -import net.opengis.gml32.TimePeriodPropertyType; -import net.opengis.gml32.TimePeriodType; -import net.opengis.gml32.TimePositionType; -import net.opengis.gml32.TimePrimitivePropertyType; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.w3c.dom.Document; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import aero.aixm511.AirportHeliportTimeSlicePropertyType; -import aero.aixm511.AirportHeliportTimeSliceType; -import aero.aixm511.AirportHeliportType; -import aero.aixm511.CodeAirportHeliportDesignatorType; -import aero.aixm511.CodeIATAType; -import aero.aixm511.CodeICAOType; -import aero.aixm511.CodeVerticalDatumType; -import aero.aixm511.ElevatedPointPropertyType; -import aero.aixm511.ElevatedPointType; -import aero.aixm511.SurfacePropertyType; -import aero.aixm511.SurfaceType; -import aero.aixm511.TextNameType; -import aero.aixm511.ValDistanceVerticalType; -import fi.fmi.avi.converter.AviMessageSpecificConverter; -import fi.fmi.avi.converter.ConversionException; -import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; -import fi.fmi.avi.converter.ConversionResult; -import fi.fmi.avi.model.Aerodrome; -import fi.fmi.avi.model.AviationWeatherMessageOrCollection; -import fi.fmi.avi.model.CircleByCenterPoint; -import fi.fmi.avi.model.CoordinateReferenceSystem; -import fi.fmi.avi.model.ElevatedPoint; -import fi.fmi.avi.model.Geometry; -import fi.fmi.avi.model.NumericMeasure; -import fi.fmi.avi.model.PartialOrCompleteTimeInstant; -import fi.fmi.avi.model.PartialOrCompleteTimePeriod; -import fi.fmi.avi.model.PointGeometry; -import fi.fmi.avi.model.PolygonGeometry; -import fi.fmi.avi.model.Winding; -import fi.fmi.avi.model.immutable.NumericMeasureImpl; -import fi.fmi.avi.model.taf.TAF; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.UUID; /** * Common functionality for serializing aviation messages into IWXXM. */ public abstract class AbstractIWXXMSerializer extends IWXXMConverterBase implements AviMessageSpecificConverter { - private static final Logger LOG = LoggerFactory.getLogger(AbstractIWXXMSerializer.class); - - protected static SurfacePropertyType createSurface(final Geometry geom, final String id) throws IllegalArgumentException { - SurfacePropertyType retval = null; - if (geom != null) { - retval = create(SurfacePropertyType.class, spt -> spt.setSurface(createAndWrap(SurfaceType.class, sft -> { - geom.getCrs().ifPresent(crs -> setCrsToType(sft, crs)); - sft.setId(id); - final JAXBElement spapt; - if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass()) || PointGeometry.class.isAssignableFrom(geom.getClass())) { - final List centerPointCoords; - final LengthType radius; - if (CircleByCenterPoint.class.isAssignableFrom(geom.getClass())) { - final CircleByCenterPoint cbcp = (CircleByCenterPoint) geom; - centerPointCoords = cbcp.getCenterPointCoordinates(); - radius = asMeasure(cbcp.getRadius(), LengthType.class); - } else { - //Create a zero-radius circle if a point geometry is given - final PointGeometry point = (PointGeometry) geom; - centerPointCoords = point.getCoordinates(); - radius = asMeasure(NumericMeasureImpl.of(0.0, "[nmi_i]"), LengthType.class); - } - - final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior( - create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(RingType.class, rt -> rt.getCurveMember() - .add(create(CurvePropertyType.class, curvept -> curvept.setAbstractCurve(createAndWrap(CurveType.class, curvet -> { - curvet.setId(UUID_PREFIX + UUID.randomUUID().toString()); - curvet.setSegments(create(CurveSegmentArrayPropertyType.class, - curvesat -> curvesat.getAbstractCurveSegment().add(createAndWrap(CircleByCenterPointType.class, cbcpt -> { - cbcpt.setPos(create(DirectPositionType.class, dpt -> dpt.getValue().addAll(centerPointCoords))); - cbcpt.setNumArc(BigInteger.valueOf(1)); - cbcpt.setRadius(radius); - })))); - }))))))))); - spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); - } else if (PolygonGeometry.class.isAssignableFrom(geom.getClass())) { //Polygon - final PolygonGeometry polygon = (PolygonGeometry) geom; - final JAXBElement ppt = createAndWrap(PolygonPatchType.class, poly -> poly.setExterior( - create(AbstractRingPropertyType.class, arpt -> arpt.setAbstractRing(createAndWrap(LinearRingType.class, lrt -> { - final DirectPositionListType dplt = create(DirectPositionListType.class, - dpl -> dpl.getValue().addAll(polygon.getExteriorRingPositions(Winding.COUNTERCLOCKWISE))); - lrt.setPosList(dplt); - }))))); - spapt = createAndWrap(SurfacePatchArrayPropertyType.class, "createPatches", _spapt -> _spapt.getAbstractSurfacePatch().add(ppt)); - } else { - throw new IllegalArgumentException("Unable to create a Surface from geometry of type " + geom.getClass().getCanonicalName()); - } - if (spapt != null) { - sft.setPatches(spapt); - } - }))); - } - return retval; - } protected static T asMeasure(final NumericMeasure input, final Class measureType) { return create(measureType, measure -> { @@ -172,113 +55,16 @@ protected static Optional toIWXXMDateTime(final PartialOrCompleteTimeIns } protected static Optional startToIWXXMDateTime(final PartialOrCompleteTimePeriod period) { - //noinspection RedundantTypeArguments - return period.getStartTime(). flatMap(AbstractIWXXMSerializer::toIWXXMDateTime); + return period.getStartTime().flatMap(AbstractIWXXMSerializer::toIWXXMDateTime); } protected static Optional endToIWXXMDateTime(final PartialOrCompleteTimePeriod period) { - //noinspection RedundantTypeArguments - return period.getEndTime(). flatMap(AbstractIWXXMSerializer::toIWXXMDateTime); - } - - protected static Optional elevationToValDistanceVertical(final ElevatedPoint elevatedPoint) { - return elevatedPoint == null - ? Optional.empty() - : valDistanceVertical(elevatedPoint.getElevationValue().orElse(Double.NaN), elevatedPoint.getElevationUom().orElse("")); + return period.getEndTime().flatMap(AbstractIWXXMSerializer::toIWXXMDateTime); } - protected static Optional toValDistanceVertical(final NumericMeasure numericMeasure) { - return numericMeasure == null ? Optional.empty() : valDistanceVertical(numericMeasure.getValue(), numericMeasure.getUom()); - } - - protected static Optional valDistanceVertical(final Double value, final String uom) { - return valDistanceVertical(value == null ? Double.NaN : value, uom); - } - - protected static Optional valDistanceVertical(final double value, final String uom) { - if (Double.isNaN(value)) { - return Optional.empty(); - } - final ValDistanceVerticalType type = create(ValDistanceVerticalType.class); - final DecimalFormat format = new DecimalFormat("", DecimalFormatSymbols.getInstance(Locale.US)); - format.setMinimumIntegerDigits(1); - format.setMinimumFractionDigits(0); - format.setMaximumFractionDigits(4); - format.setGroupingUsed(false); - type.setValue(format.format(value)); - if (uom != null && !uom.isEmpty()) { - type.setUom(uom.toUpperCase(Locale.US)); - } - return Optional.of(type); - } - - protected static ValDistanceVerticalType nilValDistanceVertical() { - final ValDistanceVerticalType type = create(ValDistanceVerticalType.class); - // TODO: how to set xsi:nil="true"? - type.setNilReason("unknown"); - type.setUom("OTHER"); - return type; - } - - @SuppressWarnings("unchecked") - protected Document renderXMLDocument(final Object input, final ConversionHints hints) throws ConversionException { - final StringWriter sw = new StringWriter(); - try { - final Marshaller marshaller = getJAXBContext().createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, getSchemaInfo().getCombinedSchemaLocations()); - marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", getNamespaceContext()); - marshaller.marshal(wrap(input, (Class) input.getClass()), sw); - return asCleanedUpXML(sw.toString(), hints); - } catch (final JAXBException e) { - throw new ConversionException("Exception in rendering to DOM", e); - } - } - - protected void setAerodromeData(final AirportHeliportType aerodrome, final Aerodrome input, final String aerodromeId, final String timeSliceIdPrefix, - final String elevatedPointIdPrefix) { - if (input == null) { - return; - } - aerodrome.setId(aerodromeId); - aerodrome.getTimeSlice() - .add(create(AirportHeliportTimeSlicePropertyType.class, - prop -> prop.setAirportHeliportTimeSlice(create(AirportHeliportTimeSliceType.class, timeSlice -> { - timeSlice.setId(timeSliceIdPrefix + UUID.randomUUID()); - timeSlice.setValidTime(create(TimePrimitivePropertyType.class)); - timeSlice.setInterpretation("SNAPSHOT"); - timeSlice.setDesignator(create(CodeAirportHeliportDesignatorType.class, designator -> designator.setValue(input.getDesignator()))); - input.getName() - .ifPresent(inputName -> timeSlice.setPortName( - create(TextNameType.class, name -> name.setValue(inputName.toUpperCase(Locale.US))))); - input.getLocationIndicatorICAO() - .ifPresent(inputLocator -> timeSlice.setLocationIndicatorICAO( - create(CodeICAOType.class, locator -> locator.setValue(inputLocator)))); - - input.getDesignatorIATA() - .ifPresent(inputDesignator -> timeSlice.setDesignatorIATA( - create(CodeIATAType.class, designator -> designator.setValue(inputDesignator)))); - valDistanceVertical(input.getFieldElevationValue().orElse(Double.NaN), input.getFieldElevationUom().orElse(""))// - .ifPresent(timeSlice::setFieldElevation); - - input.getReferencePoint() - .ifPresent(inputPosition -> timeSlice.setARP(create(ElevatedPointPropertyType.class, - elevatedPointProp -> elevatedPointProp.setElevatedPoint(create(ElevatedPointType.class, point -> { - point.setId(elevatedPointIdPrefix + UUID.randomUUID()); - inputPosition.getCrs().ifPresent(crs -> setCrsToType(point, crs)); - if (inputPosition.getCoordinates() != null) { - point.setPos( - create(DirectPositionType.class, pos -> pos.getValue().addAll(inputPosition.getCoordinates()))); - } - elevationToValDistanceVertical(inputPosition).ifPresent(point::setElevation); - inputPosition.getVerticalDatum() - .ifPresent(verticalDatum -> point.setVerticalDatum( - create(CodeVerticalDatumType.class, verticalCode -> verticalCode.setValue(verticalDatum)))); - }))))); - })))); - } + protected abstract Document renderXMLDocument(final Object input, final ConversionHints hints) throws ConversionException; - private Document asCleanedUpXML(final String input, final ConversionHints hints) throws ConversionException { + protected Document asCleanedUpXML(final String input, final ConversionHints hints) throws ConversionException { try { final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); @@ -317,7 +103,7 @@ public void createTimeInstantProperty(final TAF input, final TimeInstantProperty final TimeInstantType ti = create(TimeInstantType.class); final TimePositionType tp = create(TimePositionType.class); input.getIssueTime()// - . flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// .ifPresent(time -> tp.getValue().add(time)); ti.setTimePosition(tp); ti.setId(id); @@ -325,7 +111,7 @@ public void createTimeInstantProperty(final TAF input, final TimeInstantProperty } public void createTimePeriodPropertyType(final TimePeriodPropertyType prop, final PartialOrCompleteTimeInstant start, - final PartialOrCompleteTimeInstant end, final String id) { + final PartialOrCompleteTimeInstant end, final String id) { final TimePeriodType tp = create(TimePeriodType.class); tp.setId(id); final TimePositionType beginPos = create(TimePositionType.class); @@ -342,7 +128,7 @@ public void createTimePeriodPropertyType(final TimePeriodPropertyType prop, fina } protected static String getUUID() { - return "uuid."+UUID.randomUUID().toString(); + return "uuid." + UUID.randomUUID(); } protected abstract InputStream getCleanupTransformationStylesheet(final ConversionHints hints) throws ConversionException; diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMConverterBase.java b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMConverterBase.java index cccf1f20..07e45290 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMConverterBase.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMConverterBase.java @@ -1,58 +1,29 @@ package fi.fmi.avi.converter.iwxxm; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.StringReader; -import java.io.StringWriter; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Optional; -import java.util.Set; -import java.util.Spliterator; -import java.util.Spliterators; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.stream.Collector; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; +import fi.fmi.avi.converter.ConversionException; +import fi.fmi.avi.converter.ConversionHints; +import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.IssueList; +import fi.fmi.avi.model.PartialOrCompleteTimeInstant; +import fi.fmi.avi.model.PartialOrCompleteTimePeriod; +import net.opengis.gml32.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.w3c.dom.*; +import org.xml.sax.ErrorHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; import javax.xml.XMLConstants; -import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; import javax.xml.bind.ValidationEvent; import javax.xml.bind.ValidationEventHandler; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Templates; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.URIResolver; +import javax.xml.transform.*; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; @@ -62,33 +33,22 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; - -import net.opengis.gml32.TimeInstantPropertyType; -import net.opengis.gml32.TimeInstantType; -import net.opengis.gml32.TimePeriodPropertyType; -import net.opengis.gml32.TimePeriodType; -import net.opengis.gml32.TimePositionType; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.util.StringUtils; -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.ErrorHandler; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import org.xml.sax.SAXParseException; - -import fi.fmi.avi.converter.ConversionException; -import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; -import fi.fmi.avi.converter.IssueList; -import fi.fmi.avi.model.PartialOrCompleteTimeInstant; -import fi.fmi.avi.model.PartialOrCompleteTimePeriod; +import java.io.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collector; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Helpers for creating and handling JAXB generated content classes. @@ -117,8 +77,6 @@ public abstract class IWXXMConverterBase { private static final Map OBJECT_FACTORY_MAP = new HashMap<>(); private static final HashMap IWXXM_TEMPLATES = new HashMap<>(); - private static JAXBContext jaxbCtx = null; - static { if (System.getSecurityManager() != null) { F_SECURE_PROCESSING = true; @@ -130,27 +88,6 @@ public abstract class IWXXMConverterBase { F_SECURE_PROCESSING = false; } } - - /** - * Singleton for accessing the shared JAXBContext for IWXXM JAXB handling. - * - * NOTE: this can take several seconds when done for the first time after JVM start, - * needs to scan all the jars in classpath. - * - * @return the context - * - * @throws JAXBException - * if the context cannot be created - */ - public static synchronized JAXBContext getJAXBContext() throws JAXBException { - if (jaxbCtx == null) { - jaxbCtx = JAXBContext.newInstance("icao.iwxxm21:icao.iwxxm30:aero.aixm511:net.opengis.gml32:org.iso19139.ogc2007.gmd:org.iso19139.ogc2007.gco:org" - + ".iso19139.ogc2007.gss:org.iso19139.ogc2007.gts:org.iso19139.ogc2007.gsr:net.opengis.om20:net.opengis.sampling:net.opengis.sampling" - + ".spatial:wmo.metce2013:wmo.opm2013:wmo.collect2014:org.w3c.xlink11"); - } - return jaxbCtx; - } - public static T create(final Class clz) throws IllegalArgumentException { return create(clz, null); } @@ -264,38 +201,6 @@ public void fatalError(final SAXParseException exception) throws SAXException { return retval; } - protected static IssueList validateJAXBObjectAgainstSchemaAndSchematron(final S input, final Class clz, final XMLSchemaInfo schemaInfo, - final ConversionHints hints) { - final IssueList retval = new IssueList(); - try { - final Marshaller marshaller = getJAXBContext().createMarshaller(); - marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); - marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, schemaInfo.getCombinedSchemaLocations()); - marshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new IWXXMNamespaceContext()); - - marshaller.setSchema(schemaInfo.getSchema()); - final ConverterValidationEventHandler eventHandler = new ConverterValidationEventHandler(retval); - marshaller.setEventHandler(eventHandler); - - final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - final DocumentBuilder db = dbf.newDocumentBuilder(); - final Document dom = db.newDocument(); - - //Marshall to run the validation: - marshaller.marshal(wrap(input, clz), dom); - - retval.addAll(eventHandler.getIssues()); - - //Schematron validation: - retval.addAll(validateAgainstIWXXMSchematron(dom, schemaInfo, hints)); - } catch (final RuntimeException | JAXBException | SAXException | ParserConfigurationException e) { - throw new RuntimeException("Error in validating document", e); - } - return retval; - } - protected static IssueList validateDOMAgainstSchemaAndSchematron(final Document input, final XMLSchemaInfo schemaInfo, final ConversionHints hints) throws ConversionException { final IssueList retval = new IssueList(); diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMNamespaceContext.java b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMNamespaceContext.java index 3734690d..98d88146 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMNamespaceContext.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMNamespaceContext.java @@ -1,18 +1,10 @@ package fi.fmi.avi.converter.iwxxm; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import com.sun.xml.bind.marshaller.NamespacePrefixMapper; import javax.xml.namespace.NamespaceContext; - -import com.sun.xml.bind.marshaller.NamespacePrefixMapper; +import java.util.*; +import java.util.stream.Collectors; /** * Helper for using per-defined XML Schema namespace prefixes in IWXXM documents. @@ -31,6 +23,7 @@ public class IWXXMNamespaceContext extends NamespacePrefixMapper implements Name m.put("http://www.aixm.aero/schema/5.1.1", "aixm"); m.put("http://icao.int/iwxxm/2.1", "iwxxm"); m.put("http://icao.int/iwxxm/3.0", "iwxxm30"); + m.put("http://icao.int/iwxxm/2023-1", "iwxxm2023_1"); m.put("http://def.wmo.int/opm/2013", "opm"); m.put("http://def.wmo.int/metce/2013", "metce"); m.put("http://def.wmo.int/collect/2014", "collect"); @@ -40,7 +33,7 @@ public class IWXXMNamespaceContext extends NamespacePrefixMapper implements Name m.put("http://purl.oclc.org/dsdl/svrl", "svrl"); final Set duplicates = findDuplicatePrefixes(m); if (duplicates.size() > 0) { - throw new RuntimeException("The default namespace-prefix mapping contains duplicate prefixes, this is not allowed: " + duplicates.toString()); + throw new RuntimeException("The default namespace-prefix mapping contains duplicate prefixes, this is not allowed: " + duplicates); } DEFAULT_MAPPING = Collections.unmodifiableMap(m); } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolver.java b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolver.java index c9b3e3da..92a5cb26 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolver.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolver.java @@ -1,46 +1,28 @@ package fi.fmi.avi.converter.iwxxm; -import java.io.CharArrayReader; -import java.io.CharArrayWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.net.URL; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - import org.apache.commons.io.IOUtils; import org.w3c.dom.ls.LSInput; import org.w3c.dom.ls.LSResourceResolver; +import java.io.*; +import java.net.URL; +import java.util.EnumSet; + /** * Resolves the locations of the IWXXM XML Schema files within * the JAXB generated jar files using Java ClassLoader. Uses a memory * cache internally to avoid repeated class path searches * (keeps the full XML Schema file contents in memory as char arrays). */ -public class IWXXMSchemaResourceResolver implements LSResourceResolver { +public abstract class IWXXMSchemaResourceResolver implements LSResourceResolver { - private static final ConcurrentMap cache = new ConcurrentHashMap<>(); - //Singleton - private static IWXXMSchemaResourceResolver instance; + public abstract EnumSet ignoredNamespaceLocations(); - private IWXXMSchemaResourceResolver() { - } - - public synchronized static IWXXMSchemaResourceResolver getInstance() { - if (instance == null) { - instance = new IWXXMSchemaResourceResolver(); - } - return instance; - } - - @Override - public LSInput resolveResource(final String type, final String namespaceURI, final String publicId, final String systemId, final String baseURI) { - final NamespaceLocation ns = NamespaceLocation.forURI(namespaceURI); - if (ns != null) { - return cache.computeIfAbsent(ns.getFullPathFor(systemId), - key -> new ClassLoaderResourceInput(ns.getFinderClass(), ns.getFullPathFor(systemId), publicId, systemId, baseURI)); + protected NamespaceLocation forURI(final String namespaceURI) { + for (final NamespaceLocation namespace : EnumSet.complementOf(ignoredNamespaceLocations())) { + if (namespace.getNamespaceURI().equals(namespaceURI)) { + return namespace; + } } return null; } @@ -58,12 +40,14 @@ public enum NamespaceLocation { SAMPLING20("http://www.opengis.net/sampling/2.0", "net/opengis/sampling/2.0/", net.opengis.sampling.SamplingFeatureComplexType.class), SAMPLING_SPATIAL20("http://www.opengis.net/samplingSpatial/2.0", "net/opengis/samplingSpatial/2.0/", net.opengis.sampling.spatial.SFSpatialSamplingFeatureType.class), - AIXM51("http://www.aixm.aero/schema/5.1.1", "aero/aixm/schema/5.1.1b/", aero.aixm511.CodeICAOType.class), + AIXM511WX("http://www.aixm.aero/schema/5.1.1", "aero/aixm/schema/5.1.1b/", aero.aixm511.CodeICAOType.class), + AIXM511FULL("http://www.aixm.aero/schema/5.1.1", "aero/aixm/schema/5.1.1/", aero.aixm511full.CodeICAOType.class), METCE12("http://def.wmo.int/metce/2013", "int/wmo/metce/1.2/", wmo.metce2013.ProcessType.class), COLLECT12("http://def.wmo.int/collect/2014", "int/wmo/collect/1.2/", wmo.collect2014.MeteorologicalBulletinType.class), OPM12("http://def.wmo.int/opm/2013", "int/wmo/opm/1.2/", wmo.opm2013.AbstractObservablePropertyPropertyType.class), IWXXM21("http://icao.int/iwxxm/2.1", "int/icao/iwxxm/2.1.1/", icao.iwxxm21.TAFType.class), - IWXXM30("http://icao.int/iwxxm/3.0", "int/icao/iwxxm/3.0.0/", icao.iwxxm30.SpaceWeatherAdvisoryType.class); + IWXXM30("http://icao.int/iwxxm/3.0", "int/icao/iwxxm/3.0.0/", icao.iwxxm30.SpaceWeatherAdvisoryType.class), + IWXXM2023_1("http://icao.int/iwxxm/2023-1", "int/icao/iwxxm/2023_1/", icao.iwxxm2023_1.AIRMETType.class); private final String namespaceURI; private final String pathPrefix; @@ -75,15 +59,6 @@ public enum NamespaceLocation { this.finderClass = finderClass; } - public static NamespaceLocation forURI(final String namespaceURI) { - for (final NamespaceLocation n : values()) { - if (n.getNamespaceURI().equals(namespaceURI)) { - return n; - } - } - return null; - } - public String getFullPathFor(final String systemId) { return this.pathPrefix + systemId.substring(systemId.lastIndexOf('/') + 1); } @@ -101,7 +76,7 @@ public Class getFinderClass() { } } - private static class ClassLoaderResourceInput implements LSInput { + protected static class ClassLoaderResourceInput implements LSInput { private final URL url; private final String publicId; private final String systemId; diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Full.java b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Full.java new file mode 100644 index 00000000..cf4f2132 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Full.java @@ -0,0 +1,45 @@ +package fi.fmi.avi.converter.iwxxm; + +import org.w3c.dom.ls.LSInput; + +import java.util.EnumSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Resolves the locations of the IWXXM XML Schema files within + * the JAXB generated jar files using Java ClassLoader. Uses a memory + * cache internally to avoid repeated class path searches + * (keeps the full XML Schema file contents in memory as char arrays). + */ +public class IWXXMSchemaResourceResolverAixm511Full extends IWXXMSchemaResourceResolver { + + private static final ConcurrentMap cache = new ConcurrentHashMap<>(); + private static IWXXMSchemaResourceResolverAixm511Full instance; + + private IWXXMSchemaResourceResolverAixm511Full() { + } + + public synchronized static IWXXMSchemaResourceResolverAixm511Full getInstance() { + if (instance == null) { + instance = new IWXXMSchemaResourceResolverAixm511Full(); + } + return instance; + } + + @Override + public LSInput resolveResource(final String type, final String namespaceURI, final String publicId, final String systemId, final String baseURI) { + final NamespaceLocation ns = forURI(namespaceURI); + if (ns != null) { + return cache.computeIfAbsent(ns.getFullPathFor(systemId), + key -> new ClassLoaderResourceInput(ns.getFinderClass(), ns.getFullPathFor(systemId), publicId, systemId, baseURI)); + } + return null; + } + + @Override + public EnumSet ignoredNamespaceLocations() { + return EnumSet.of(NamespaceLocation.AIXM511WX); + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Wx.java b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Wx.java new file mode 100644 index 00000000..32ea48a2 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/IWXXMSchemaResourceResolverAixm511Wx.java @@ -0,0 +1,44 @@ +package fi.fmi.avi.converter.iwxxm; + +import org.w3c.dom.ls.LSInput; + +import java.util.EnumSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Resolves the locations of the IWXXM XML Schema files within + * the JAXB generated jar files using Java ClassLoader. Uses a memory + * cache internally to avoid repeated class path searches + * (keeps the full XML Schema file contents in memory as char arrays). + */ +public class IWXXMSchemaResourceResolverAixm511Wx extends IWXXMSchemaResourceResolver { + + private static final ConcurrentMap cache = new ConcurrentHashMap<>(); + private static IWXXMSchemaResourceResolverAixm511Wx instance; + + private IWXXMSchemaResourceResolverAixm511Wx() { + } + + public synchronized static IWXXMSchemaResourceResolverAixm511Wx getInstance() { + if (instance == null) { + instance = new IWXXMSchemaResourceResolverAixm511Wx(); + } + return instance; + } + + @Override + public LSInput resolveResource(final String type, final String namespaceURI, final String publicId, final String systemId, final String baseURI) { + final NamespaceLocation ns = forURI(namespaceURI); + if (ns != null) { + return cache.computeIfAbsent(ns.getFullPathFor(systemId), + key -> new ClassLoaderResourceInput(ns.getFinderClass(), ns.getFullPathFor(systemId), publicId, systemId, baseURI)); + } + return null; + } + + @Override + public EnumSet ignoredNamespaceLocations() { + return EnumSet.of(NamespaceLocation.AIXM511FULL); + } +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/XMLSchemaInfo.java b/src/main/java/fi/fmi/avi/converter/iwxxm/XMLSchemaInfo.java index 05eedc1a..d11c3b98 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/XMLSchemaInfo.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/XMLSchemaInfo.java @@ -1,18 +1,11 @@ package fi.fmi.avi.converter.iwxxm; -import static java.util.Objects.requireNonNull; - -import java.io.Closeable; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.regex.Pattern; -import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; import javax.annotation.Nullable; import javax.xml.XMLConstants; @@ -20,13 +13,14 @@ import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; +import java.io.Closeable; +import java.io.IOException; +import java.net.URL; +import java.util.*; +import java.util.regex.Pattern; +import java.util.stream.Collectors; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.xml.sax.SAXException; -import org.xml.sax.SAXNotRecognizedException; -import org.xml.sax.SAXNotSupportedException; -import org.xml.sax.SAXParseException; +import static java.util.Objects.requireNonNull; public class XMLSchemaInfo { public static final String SCHEMA_LOCATION_ATTRIBUTE = "schemaLocation"; @@ -39,13 +33,8 @@ public class XMLSchemaInfo { private final Map schemaLocations = new LinkedHashMap<>(); private final List schematronRules = new ArrayList<>(); - public XMLSchemaInfo() { - this(false); - } - - public XMLSchemaInfo(final boolean secureProcessing) { - final IWXXMSchemaResourceResolver resolver = IWXXMSchemaResourceResolver.getInstance(); - schemaFactory.setResourceResolver(resolver); + public XMLSchemaInfo(final IWXXMSchemaResourceResolver schemaResourceResolver, final boolean secureProcessing) { + schemaFactory.setResourceResolver(schemaResourceResolver); try { schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, secureProcessing); } catch (SAXNotSupportedException | SAXNotRecognizedException e) { diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMAixm511WxSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMAixm511WxSerializer.java new file mode 100644 index 00000000..d9c175d1 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMAixm511WxSerializer.java @@ -0,0 +1,21 @@ +package fi.fmi.avi.converter.iwxxm.bulletin; + +import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolverAixm511Wx; +import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; +import fi.fmi.avi.model.AviationWeatherMessage; +import fi.fmi.avi.model.bulletin.MeteorologicalBulletin; +import wmo.collect2014.MeteorologicalBulletinType; + +public abstract class AbstractBulletinIWXXMAixm511WxSerializer> + extends AbstractBulletinIWXXMSerializer { + + @Override + protected XMLSchemaInfo getSchemaInfo() { + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Wx.getInstance(), F_SECURE_PROCESSING); + schemaInfo.addSchemaSource(MeteorologicalBulletinType.class.getResource("/int/wmo/collect/1.2/collect.xsd")); + schemaInfo.addSchematronRule(MeteorologicalBulletinType.class.getResource("/schematron/xslt/int/wmo/collect/1.2/rule/collect.xsl")); + schemaInfo.addSchemaLocation("http://def.wmo.int/collect/2014", "http://schemas.wmo.int/collect/1.2/collect.xsd"); + return schemaInfo; + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMSerializer.java index cf04768a..be5bdd4b 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/AbstractBulletinIWXXMSerializer.java @@ -1,5 +1,22 @@ package fi.fmi.avi.converter.iwxxm.bulletin; +import fi.fmi.avi.converter.*; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.IWXXMConverterBase; +import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; +import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; +import fi.fmi.avi.model.AviationWeatherMessage; +import fi.fmi.avi.model.bulletin.MeteorologicalBulletin; +import fi.fmi.avi.util.GTSExchangeFileInfo; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.time.LocalDateTime; @@ -11,31 +28,6 @@ import java.util.Set; import java.util.UUID; -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Attr; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.xml.sax.SAXException; - -import fi.fmi.avi.converter.AviMessageSpecificConverter; -import fi.fmi.avi.converter.ConversionException; -import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; -import fi.fmi.avi.converter.ConversionResult; -import fi.fmi.avi.converter.IssueList; -import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; -import fi.fmi.avi.converter.iwxxm.IWXXMConverterBase; -import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; -import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; -import fi.fmi.avi.model.AviationWeatherMessage; -import fi.fmi.avi.model.bulletin.MeteorologicalBulletin; -import fi.fmi.avi.util.GTSExchangeFileInfo; -import wmo.collect2014.MeteorologicalBulletinType; - /** * @param * bulletin content model type @@ -159,13 +151,7 @@ public ConversionResult convertMessage(final S input, final ConversionHints h return result; } - private XMLSchemaInfo getSchemaInfo() { - final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(F_SECURE_PROCESSING); - schemaInfo.addSchemaSource(MeteorologicalBulletinType.class.getResource("/int/wmo/collect/1.2/collect.xsd")); - schemaInfo.addSchematronRule(MeteorologicalBulletinType.class.getResource("/schematron/xslt/int/wmo/collect/1.2/rule/collect.xsl")); - schemaInfo.addSchemaLocation("http://def.wmo.int/collect/2014", "http://schemas.wmo.int/collect/1.2/collect.xsd"); - return schemaInfo; - } + protected abstract XMLSchemaInfo getSchemaInfo(); protected abstract T aggregateAsBulletin(final Document collection, final List messages, final ConversionHints hints) throws ConversionException; diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMDOMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMDOMSerializer.java index fe4c664a..e143cdd2 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMDOMSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMDOMSerializer.java @@ -1,27 +1,25 @@ package fi.fmi.avi.converter.iwxxm.bulletin.v1_2; -import java.util.List; - -import javax.xml.XMLConstants; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - import fi.fmi.avi.converter.ConversionException; import fi.fmi.avi.converter.ConversionHints; import fi.fmi.avi.converter.IssueList; import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; -import fi.fmi.avi.converter.iwxxm.bulletin.AbstractBulletinIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.bulletin.AbstractBulletinIWXXMAixm511WxSerializer; import fi.fmi.avi.model.AviationWeatherMessage; import fi.fmi.avi.model.bulletin.MeteorologicalBulletin; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import java.util.List; public class BulletinIWXXMDOMSerializer> - extends AbstractBulletinIWXXMSerializer { + extends AbstractBulletinIWXXMAixm511WxSerializer { @Override protected Document aggregateAsBulletin(final Document collection, final List messages, final ConversionHints hints) { diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMStringSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMStringSerializer.java index aea3b9a3..d416925d 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMStringSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/bulletin/v1_2/BulletinIWXXMStringSerializer.java @@ -1,24 +1,23 @@ package fi.fmi.avi.converter.iwxxm.bulletin.v1_2; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.StringReader; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.w3c.dom.Document; - import fi.fmi.avi.converter.ConversionException; import fi.fmi.avi.converter.ConversionHints; import fi.fmi.avi.converter.IssueList; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; -import fi.fmi.avi.converter.iwxxm.bulletin.AbstractBulletinIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.bulletin.AbstractBulletinIWXXMAixm511WxSerializer; import fi.fmi.avi.model.AviationWeatherMessage; import fi.fmi.avi.model.bulletin.MeteorologicalBulletin; +import org.w3c.dom.Document; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class BulletinIWXXMStringSerializer> - extends AbstractBulletinIWXXMSerializer { + extends AbstractBulletinIWXXMAixm511WxSerializer { private static final Pattern XML_DECLARATION_PATTERN = Pattern.compile("^\\s*<\\?xml\\s[^>]*\\?>(?:[ \t]*(?:\r\n|\r|\n))?"); private static final int INDENT_LENGTH = 2; diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMConverter.java b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMConverter.java index 48659c49..fbaed08f 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMConverter.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMConverter.java @@ -1,9 +1,5 @@ package fi.fmi.avi.converter.iwxxm.conf; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.w3c.dom.Document; - import fi.fmi.avi.converter.ConversionSpecification; import fi.fmi.avi.model.GenericAviationWeatherMessage; import fi.fmi.avi.model.bulletin.GenericMeteorologicalBulletin; @@ -16,6 +12,9 @@ import fi.fmi.avi.model.taf.TAF; import fi.fmi.avi.model.taf.TAFBulletin; import icao.iwxxm21.TAFType; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.w3c.dom.Document; /** * Created by rinne on 10/02/17. @@ -187,6 +186,18 @@ public class IWXXMConverter { public static final ConversionSpecification SIGMET_POJO_TO_IWXXM30_DOM = new ConversionSpecification<>(SIGMET.class, Document.class, null, "SIGMET, XML/IWXXM 3.0"); + /** + * Pre-configured spec for {@link SIGMET} to IWXXM 2023-1 XML format SIGMET document String. + */ + public static final ConversionSpecification SIGMET_POJO_TO_IWXXM2023_1_STRING = new ConversionSpecification<>(SIGMET.class, String.class, null, + "SIGMET, XML/IWXXM 2023-1"); + + /** + * Pre-configured spec for {@link SIGMET} to IWXXM 2023-1 XML format SIGMET document DOM Node. + */ + public static final ConversionSpecification SIGMET_POJO_TO_IWXXM2023_1_DOM = new ConversionSpecification<>(SIGMET.class, Document.class, null, + "SIGMET, XML/IWXXM 2023-1"); + /** * Pre-configured spec for {@link AIRMET} to IWXXM 2.1 XML format AIRMET document String. */ @@ -206,11 +217,23 @@ public class IWXXMConverter { "AIRMET, XML/IWXXM 3.0"); /** - * Pre-configured spec for {@link AIRMET} to IWXXM 2.1 XML format AIRMET document DOM Node. + * Pre-configured spec for {@link AIRMET} to IWXXM 3.0 XML format AIRMET document DOM Node. */ public static final ConversionSpecification AIRMET_POJO_TO_IWXXM30_DOM = new ConversionSpecification<>(AIRMET.class, Document.class, null, "AIRMET, XML/IWXXM 3.0"); + /** + * Pre-configured spec for {@link AIRMET} to IWXXM 2023-1 XML format AIRMET document String. + */ + public static final ConversionSpecification AIRMET_POJO_TO_IWXXM2023_1_STRING = new ConversionSpecification<>(AIRMET.class, String.class, null, + "AIRMET, XML/IWXXM 2023-1"); + + /** + * Pre-configured spec for {@link AIRMET} to IWXXM 2023-1 XML format AIRMET document DOM Node. + */ + public static final ConversionSpecification AIRMET_POJO_TO_IWXXM2023_1_DOM = new ConversionSpecification<>(AIRMET.class, Document.class, null, + "AIRMET, XML/IWXXM 2023-1"); + // ******************* // Space weather // ******************* diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMSIGMETAIRMETConverter.java b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMSIGMETAIRMETConverter.java index e779d553..be6b1a73 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMSIGMETAIRMETConverter.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMSIGMETAIRMETConverter.java @@ -1,16 +1,13 @@ package fi.fmi.avi.converter.iwxxm.conf; +import fi.fmi.avi.converter.AviMessageSpecificConverter; +import fi.fmi.avi.model.sigmet.AIRMET; +import fi.fmi.avi.model.sigmet.SIGMET; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.w3c.dom.Document; -import fi.fmi.avi.converter.AviMessageSpecificConverter; -import fi.fmi.avi.converter.iwxxm.v2_1.airmet.AIRMETIWXXMSerializer; -import fi.fmi.avi.converter.iwxxm.v2_1.sigmet.SIGMETIWXXMSerializer; -import fi.fmi.avi.model.sigmet.AIRMET; -import fi.fmi.avi.model.sigmet.SIGMET; - @Configuration public class IWXXMSIGMETAIRMETConverter { // Serializers: @@ -18,13 +15,13 @@ public class IWXXMSIGMETAIRMETConverter { @Bean @Qualifier("sigmetIWXXMStringSerializer") public AviMessageSpecificConverter sigmetIWXXMStringSerializer() { - return new SIGMETIWXXMSerializer.ToString(); + return new fi.fmi.avi.converter.iwxxm.v2_1.sigmet.SIGMETIWXXMSerializer.ToString(); } @Bean @Qualifier("sigmetIWXXMDOMSerializer") public AviMessageSpecificConverter sigmetIWXXMDOMSerializer() { - return new SIGMETIWXXMSerializer.ToDOM(); + return new fi.fmi.avi.converter.iwxxm.v2_1.sigmet.SIGMETIWXXMSerializer.ToDOM(); } @Bean @@ -39,6 +36,18 @@ public AviMessageSpecificConverter sigmetIWXXM30DOMSerializer( return new fi.fmi.avi.converter.iwxxm.v3_0.sigmet.SIGMETIWXXMSerializer.ToDOM(); } + @Bean + @Qualifier("sigmetIWXXM20231StringSerializer") + public AviMessageSpecificConverter sigmetIWXXM20231StringSerializer() { + return new fi.fmi.avi.converter.iwxxm.v2023_1.sigmet.SIGMETIWXXMSerializer.ToString(); + } + + @Bean + @Qualifier("sigmetIWXXM20231DOMSerializer") + public AviMessageSpecificConverter sigmetIWXXM20231DOMSerializer() { + return new fi.fmi.avi.converter.iwxxm.v2023_1.sigmet.SIGMETIWXXMSerializer.ToDOM(); + } + @Bean @Qualifier("airmetIWXXMStringSerializer") public AviMessageSpecificConverter airmetIWXXMStringSerializer() { @@ -60,6 +69,18 @@ public AviMessageSpecificConverter airmetIWXXM30StringSerializer @Bean @Qualifier("airmetIWXXM30DOMSerializer") public AviMessageSpecificConverter airmetIWXXM30DOMSerializer() { - return new AIRMETIWXXMSerializer.ToDOM(); + return new fi.fmi.avi.converter.iwxxm.v3_0.airmet.AIRMETIWXXMSerializer.ToDOM(); + } + + @Bean + @Qualifier("airmetIWXXM20231StringSerializer") + public AviMessageSpecificConverter airmetIWXXM20231StringSerializer() { + return new fi.fmi.avi.converter.iwxxm.v2023_1.airmet.AIRMETIWXXMSerializer.ToString(); + } + + @Bean + @Qualifier("airmetIWXXM20231DOMSerializer") + public AviMessageSpecificConverter airmetIWXXM20231DOMSerializer() { + return new fi.fmi.avi.converter.iwxxm.v2023_1.airmet.AIRMETIWXXMSerializer.ToDOM(); } } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMTAFConverter.java b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMTAFConverter.java index ba49b255..c69f34e3 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMTAFConverter.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/conf/IWXXMTAFConverter.java @@ -1,9 +1,5 @@ package fi.fmi.avi.converter.iwxxm.conf; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.w3c.dom.Document; - import fi.fmi.avi.converter.AviMessageSpecificConverter; import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; import fi.fmi.avi.converter.iwxxm.bulletin.v1_2.BulletinIWXXMDOMSerializer; @@ -13,7 +9,9 @@ import fi.fmi.avi.converter.iwxxm.v2_1.taf.TAFIWXXMSerializer; import fi.fmi.avi.model.taf.TAF; import fi.fmi.avi.model.taf.TAFBulletin; -import icao.iwxxm21.TAFType; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.w3c.dom.Document; @Configuration public class IWXXMTAFConverter { @@ -89,12 +87,6 @@ public AbstractIWXXMSerializer tafIWXXM30DOMSerializer() { return new fi.fmi.avi.converter.iwxxm.v3_0.taf.TAFIWXXMSerializer.ToDOM(); } - // TODO: check if this bean / class is actually used / required somewhere? - @Bean - public AviMessageSpecificConverter tafIWXXMJAXBSerializer() { - return new TAFIWXXMSerializer.ToJAXBObject(); - } - @Bean public AviMessageSpecificConverter tafBulletinIWXXMStringSerializer() { final BulletinIWXXMStringSerializer retval = new BulletinIWXXMStringSerializer<>(); diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/AbstractIWXXM20231Serializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/AbstractIWXXM20231Serializer.java new file mode 100644 index 00000000..157c9a34 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/AbstractIWXXM20231Serializer.java @@ -0,0 +1,46 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1; + +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMAixm511FullSerializer; +import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; +import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolverAixm511Full; +import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; +import fi.fmi.avi.model.AviationWeatherMessageOrCollection; +import icao.iwxxm2023_1.SIGMETType; + +import java.util.List; + +public abstract class AbstractIWXXM20231Serializer extends AbstractIWXXMAixm511FullSerializer { + + private static final String IWXXM_2023_1_SCHEMA_PATH = "/int/icao/iwxxm/2023_1/iwxxm.xsd"; + private static final String IWXXM_2023_1_SCHEMATRON_RULE_PATH = "/schematron/xslt/int/icao/iwxxm/2023_1/rule/iwxxm.xsl"; + private static IWXXMNamespaceContext nsCtx; + + private static synchronized IWXXMNamespaceContext getNSContext() { + if (nsCtx == null) { + nsCtx = new IWXXMNamespaceContext(); + nsCtx.overrideNamespacePrefix("http://icao.int/iwxxm/2.1", "iwxxm21"); + nsCtx.overrideNamespacePrefix("http://icao.int/iwxxm/3.0", "iwxxm30"); + nsCtx.overrideNamespacePrefix("http://icao.int/iwxxm/2023-1", "iwxxm"); + } + return nsCtx; + } + + @Override + public XMLSchemaInfo getSchemaInfo() { + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Full.getInstance(), F_SECURE_PROCESSING); + schemaInfo.addSchemaSource(SIGMETType.class.getResource(IWXXM_2023_1_SCHEMA_PATH)); + schemaInfo.addSchematronRule(SIGMETType.class.getResource(IWXXM_2023_1_SCHEMATRON_RULE_PATH)); + schemaInfo.addSchemaLocation("http://icao.int/iwxxm/2023-1", "http://schemas.wmo.int/iwxxm/2023-1/iwxxm.xsd"); + return schemaInfo; + } + + @Override + protected IWXXMNamespaceContext getNamespaceContext() { + return getNSContext(); + } + + protected static E getFirstOrNull(final List list) { + return list == null || list.isEmpty() ? null : list.get(0); + } + +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETIWXXMSerializer.java new file mode 100644 index 00000000..315894f9 --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETIWXXMSerializer.java @@ -0,0 +1,516 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1.airmet; + +import aero.aixm511full.ValDistanceVerticalType; +import aero.aixm511full.*; +import fi.fmi.avi.converter.*; +import fi.fmi.avi.converter.ConversionResult.Status; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; +import fi.fmi.avi.converter.iwxxm.v2023_1.AbstractIWXXM20231Serializer; +import fi.fmi.avi.model.*; +import fi.fmi.avi.model.AviationCodeListUser.AeronauticalAirmetWeatherPhenomenon; +import fi.fmi.avi.model.AviationCodeListUser.WeatherCausingVisibilityReduction; +import fi.fmi.avi.model.sigmet.AIRMET; +import fi.fmi.avi.model.sigmet.SigmetAnalysisType; +import icao.iwxxm2023_1.AirspacePropertyType; +import icao.iwxxm2023_1.AirspaceVolumePropertyType; +import icao.iwxxm2023_1.UnitPropertyType; +import icao.iwxxm2023_1.*; +import net.opengis.gml32.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; + +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.InputStream; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Optional; + +public abstract class AIRMETIWXXMSerializer extends AbstractIWXXM20231Serializer { + private static final Logger LOG = LoggerFactory.getLogger(AIRMETIWXXMSerializer.class); + + protected static TimePeriodPropertyType getTimePeriodPropertyType(final AIRMET input) { + return getATimePeriodPropertyType(input.getValidityPeriod()); + } + + protected static Optional getCancelledTimePeriodPropertyType(final AIRMET input) { + return input.getCancelledReference().map(airmetReference -> getATimePeriodPropertyType(airmetReference.getValidityPeriod())); + } + + protected static TimePeriodPropertyType getATimePeriodPropertyType(final PartialOrCompleteTimePeriod valTime) { + return create(TimePeriodPropertyType.class, prop -> { + final TimePeriodType tp = create(TimePeriodType.class); + tp.setId(getUUID()); + final TimePositionType beginPos = create(TimePositionType.class); + startToIWXXMDateTime(valTime).ifPresent(time -> beginPos.getValue().add(time)); + tp.setBeginPosition(beginPos); + final TimePositionType endPos = create(TimePositionType.class); + endToIWXXMDateTime(valTime).ifPresent(time -> endPos.getValue().add(time)); + tp.setEndPosition(endPos); + prop.setTimePeriod(tp); + }); + } + + protected abstract T render(final AIRMETType airmet, final ConversionHints hints) throws ConversionException; + + protected abstract IssueList validate(final T output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) throws ConversionException; + + /** + * Converts an AIRMET object into another format. + * + * @param input input message + * @param hints parsing hints + * @return the conversion result. + */ + @Override + public ConversionResult convertMessage(final AIRMET input, final ConversionHints hints) { + final ConversionResult result = new ConversionResult<>(); + + LOG.info("Starting conversion of AIRMET"); + if (!input.areAllTimeReferencesComplete()) { + result.addIssue(new ConversionIssue(ConversionIssue.Type.MISSING_DATA, + "All time references must be completed before converting to IWXXM")); + return result; + } + if (!input.getPhenomenon().isPresent()) { + result.addIssue(new ConversionIssue(ConversionIssue.Type.MISSING_DATA, + "An AIRMET phenomenon has to be specified")); + return result; + } + + final String airmetUuid = getUUID(); + + final AIRMETType airmet; + AeronauticalAirmetWeatherPhenomenon airmetPhenomenon = input.getPhenomenon().get(); + + airmet = create(AIRMETType.class); + airmet.setId(getUUID()); + if (input.getCancelledReference().isPresent()) { + airmet.setReportStatus(ReportStatusType.NORMAL); + airmet.setIsCancelReport(true); + airmet.setCancelledReportSequenceNumber(input.getCancelledReference().get().getSequenceNumber()); + airmet.setPhenomenon(null); + getCancelledTimePeriodPropertyType(input) + .ifPresent(airmet::setCancelledReportValidPeriod); + } else { + airmet.setReportStatus(ReportStatusType.NORMAL); + airmet.setIsCancelReport(false); + airmet.setPhenomenon(create(AeronauticalAreaWeatherPhenomenonType.class, phen -> phen.setHref( + AviationCodeListUser.CODELIST_AIRMET_PHENOMENA_ROOT + airmetPhenomenon.name()))); + } + //Use current time as issueTime if missing + final String issueTime = input.getIssueTime().flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .orElseGet(() -> toIWXXMDateTime(ZonedDateTime.now())); + + airmet.setIssueTime( + create(TimeInstantPropertyType.class, tip -> tip.setTimeInstant(create(TimeInstantType.class, ti -> { + ti.setTimePosition(create(TimePositionType.class, tp -> tp.getValue().add(issueTime))); + ti.setId(getUUID()); + })))); + + airmet.setIssuingAirTrafficServicesUnit(create(UnitPropertyType.class, prop -> prop.setUnit(create(UnitType.class, unit -> { + unit.setId(getUUID()); + unit.getTimeSlice().add(create(UnitTimeSlicePropertyType.class, sliceProp -> sliceProp.setUnitTimeSlice(create(UnitTimeSliceType.class, slice -> { + slice.setId(getUUID()); + slice.setValidTime(create(TimePrimitivePropertyType.class, tp -> { + })); + slice.setInterpretation("SNAPSHOT"); + slice.setType(create(CodeUnitType.class, codeUnitType -> codeUnitType.setValue("FIC"))); + slice.setUnitName(create(TextNameType.class, tnt -> { + tnt.setValue(input.getIssuingAirTrafficServicesUnit().getName()); + })); + slice.setDesignator( + create(CodeOrganisationDesignatorType.class, desig -> desig.setValue(input.getIssuingAirTrafficServicesUnit().getDesignator()))); + })))); + })))); + + airmet.setOriginatingMeteorologicalWatchOffice( + create(UnitPropertyType.class, prop -> prop.setUnit(create(UnitType.class, unit -> { + unit.setId(getUUID()); + unit.getTimeSlice().add(create(UnitTimeSlicePropertyType.class, + sliceProp -> sliceProp.setUnitTimeSlice(create(UnitTimeSliceType.class, slice -> { + slice.setId(getUUID()); + slice.setValidTime(create(TimePrimitivePropertyType.class, tp -> { + })); + slice.setInterpretation("SNAPSHOT"); + slice.setType(create(CodeUnitType.class, codeUnitType -> codeUnitType.setValue("MWO"))); + slice.setUnitName(create(TextNameType.class, tnt -> { + tnt.setValue(input.getMeteorologicalWatchOffice().getName()); + })); + + slice.setDesignator(create(CodeOrganisationDesignatorType.class, desig -> + desig.setValue(input.getMeteorologicalWatchOffice().getDesignator()))); + })))); + })))); + + airmet.setIssuingAirTrafficServicesRegion( + create(AirspacePropertyType.class, prop -> prop.setAirspace(create(AirspaceType.class, airspace -> { + airspace.setValidTime(null); + airspace.setId(getUUID()); + airspace.getTimeSlice() + .add(create(AirspaceTimeSlicePropertyType.class, timeSliceProp -> timeSliceProp + .setAirspaceTimeSlice(create(AirspaceTimeSliceType.class, timeSlice -> { + timeSlice.setValidTime(create(TimePrimitivePropertyType.class)); + timeSlice.setInterpretation("SNAPSHOT"); + timeSlice.setType(create(CodeAirspaceType.class, type -> type.setValue("FIR"))); + timeSlice.setAirspaceName(create(TextNameType.class, name -> name + .setValue(input.getAirspace().getName()))); + timeSlice.setId(getUUID()); + timeSlice.setDesignator(create(CodeAirspaceDesignatorType.class, desig -> desig + .setValue(input.getAirspace().getDesignator()))); + + })))); + + })))); + + airmet.setSequenceNumber( + create(StringWithNilReasonType.class, prop -> prop.setValue(input.getSequenceNumber()))); + + airmet.setValidPeriod(getTimePeriodPropertyType(input)); + + if (!input.getCancelledReference().isPresent()) { + final String analysisTime = input.getAnalysisGeometries()// + .map(AbstractIWXXM20231Serializer::getFirstOrNull)// + .flatMap(PhenomenonGeometryWithHeight::getTime)// + .flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .orElse(null); + + List ans = input.getAnalysisGeometries().get(); + + AIRMETEvolvingConditionCollectionPropertyType aeccpt = createAnalysis(input, ans, analysisTime, + airmetUuid); + airmet.setAnalysis(aeccpt); + + } + + try { + this.updateMessageMetadata(input, result, airmet); + final T rendered = this.render(airmet, hints); + result.addIssue(validate(rendered, getSchemaInfo(), hints)); + result.setConvertedMessage(rendered); + } catch (final ConversionException e) { + result.setStatus(Status.FAIL); + result.addIssue(new ConversionIssue(ConversionIssue.Type.OTHER, "Unable to render IWXXM message", e)); + } + + return result; + } + + /**************************************************************************/ + + @SuppressWarnings("unchecked") + private AIRMETEvolvingConditionCollectionPropertyType createAnalysis(AIRMET input, + final List ans, final String analysisTime, final String airmetUUID) { + + AeronauticalAirmetWeatherPhenomenon airmetPhenomenon = input.getPhenomenon().get(); + AbstractTimeObjectPropertyType phenTimeProp; + if (analysisTime != null) { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> { + final JAXBElement wrapped = createAndWrap(TimeInstantType.class, inst -> { + inst.setId(getUUID()); + ZonedDateTime validityStart = input.getValidityPeriod().getStartTime().get().getCompleteTime().get(); + ZonedDateTime phenTime = ZonedDateTime.parse(analysisTime, DateTimeFormatter.ISO_INSTANT.withZone(ZoneOffset.UTC)); + if (phenTime.isEqual(validityStart)) { + inst.setTimePosition(create(TimePositionType.class, tPos -> tPos.getValue().add(analysisTime))); + } else { + String phenomenonTimeString = validityStart.format(DateTimeFormatter.ISO_INSTANT); + inst.setTimePosition(create(TimePositionType.class, tPos -> tPos.getValue().add(phenomenonTimeString))); + } + }); + toProp.setAbstractTimeObject((JAXBElement) wrapped); + }); + } else { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> toProp.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_MISSING)); + } + AIRMETEvolvingConditionCollectionType aecct = create( + AIRMETEvolvingConditionCollectionType.class, ecct -> { + ecct.setPhenomenonTime(phenTimeProp); + ecct.setId(getUUID()); + for (PhenomenonGeometryWithHeight an : ans) { + if (an.getAnalysisType().equals(SigmetAnalysisType.FORECAST)) { + ecct.setTimeIndicator(TimeIndicatorType.FORECAST); + } else if (an.getAnalysisType().equals(SigmetAnalysisType.OBSERVATION)) { + ecct.setTimeIndicator(TimeIndicatorType.OBSERVATION); + } else { + ecct.setTimeIndicator(null); + } + ecct.getMember().add(create(AIRMETEvolvingConditionPropertyType.class, seccpt -> { + seccpt.setAIRMETEvolvingCondition(create(AIRMETEvolvingConditionType.class, sect -> { + if (an.getIntensityChange().isPresent()) { + switch (an.getIntensityChange().get()) { + case INTENSIFYING: + sect.setIntensityChange(AIRMETExpectedIntensityChangeType.INTENSIFY); + break; + case WEAKENING: + sect.setIntensityChange(AIRMETExpectedIntensityChangeType.WEAKEN); + break; + case NO_CHANGE: + sect.setIntensityChange(AIRMETExpectedIntensityChangeType.NO_CHANGE); + break; + } + } else { + sect.setIntensityChange(AIRMETExpectedIntensityChangeType.NO_CHANGE); + } + sect.setId(getUUID()); + sect.setGeometry(create(AirspaceVolumePropertyType.class, avpt -> { + avpt.setAirspaceVolume(createAirspaceVolume(an)); + })); + if (airmetPhenomenon.name().equals("SFC_VIS")) { + sect.setSurfaceVisibility(create(LengthType.class, l -> { + l.setUom(toIwxxmLevelUom(input.getVisibility().get())); + l.setValue(input.getVisibility().get().getValue()); + })); + for (WeatherCausingVisibilityReduction obsc : input.getObscuration().get()) { + sect.getSurfaceVisibilityCause().add(create(WeatherCausingVisibilityReductionType.class, w -> { + w.setHref(AviationCodeListUser.CODELIST_VALUE_WEATHERCAUSINGVISIBILITYREDUCTION + "/" + obsc.name()); + })); + } + } + if (airmetPhenomenon.name().equals("BKN_CLD") || + airmetPhenomenon.name().equals("OVC_CLD")) { + sect.setCloudBase(create(LengthType.class, b -> { + b.setUom(toIwxxmLevelUom(input.getCloudLevels().get().getCloudBase())); + b.setValue(input.getCloudLevels().get().getCloudBase().getValue()); + })); + if (input.getCloudLevels().get().getCloudBase().getValue() == 0) { + sect.setCloudBaseReference("SFC"); + } else { + sect.setCloudBaseReference("STD"); + } + sect.setCloudTop(create(LengthType.class, b -> { + b.setUom(toIwxxmLevelUom(input.getCloudLevels().get().getCloudTop())); + b.setValue(input.getCloudLevels().get().getCloudTop().getValue()); + })); + sect.setCloudTopReference("STD"); + } + if (airmetPhenomenon.name().equals("SFC_WIND")) { + sect.setSurfaceWindDirection(create(AngleType.class, a -> { + a.setUom(toIwxxmLevelUom(input.getWind().get().getDirection())); + a.setValue(input.getWind().get().getDirection().getValue()); + })); + sect.setSurfaceWindSpeed(create(SpeedType.class, a -> { + a.setUom(toIwxxmLevelUom(input.getWind().get().getSpeed())); + a.setValue(input.getWind().get().getSpeed().getValue()); + })); + + } + + if (an.getMovingDirection().isPresent()) { + final AngleWithNilReasonType angl = new AngleWithNilReasonType(); + final NumericMeasure md = an.getMovingDirection().get(); + angl.setUom(md.getUom()); + angl.setValue(md.getValue()); + sect.setDirectionOfMotion(angl); + + an.getMovingSpeed() + .ifPresent(ms -> sect.setSpeedOfMotion(create(SpeedType.class, speed -> { + if (ms.getUom().equals("KT")) { + speed.setUom("[kn_i]"); + } else if (ms.getUom().equals("KMH")) { + speed.setUom("km/h"); + } else { + speed.setUom(ms.getUom()); + } + speed.setValue(ms.getValue()); + }))); + } else { + sect.setDirectionOfMotion(create(AngleWithNilReasonType.class, angle -> { + angle.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_INAPPLICABLE); + angle.setUom("deg"); + })); + sect.setSpeedOfMotion(create(SpeedType.class, speed -> { + speed.setValue(0); + speed.setUom("[kn_i]"); + })); + } + })); + })); + } + }); + + return create(AIRMETEvolvingConditionCollectionPropertyType.class, aeccpt -> aeccpt.setAIRMETEvolvingConditionCollection(aecct)); + } + + private String toIwxxmLevelUom(NumericMeasure level) { + String uom = level.getUom(); + String iwxxmUom = uom; //Fallback to whatever is already there + switch (uom) { + case "FT": + iwxxmUom = "[ft_i]"; + break; + case "M": + iwxxmUom = "m"; + break; + case "KT": + iwxxmUom = "[kn_i]"; + break; + case "deg": + iwxxmUom = "deg"; + break; + } + return iwxxmUom; + } + + private AirspaceVolumeType createAirspaceVolume(PhenomenonGeometryWithHeight an) { + NumericMeasure lowerLevel = an.getLowerLimit().orElse(null); + NumericMeasure upperLevel = an.getUpperLimit().orElse(null); + AviationCodeListUser.RelationalOperator lowerLimitOperator = an.getLowerLimitOperator().orElse(null); + AviationCodeListUser.RelationalOperator upperLimitOperator = an.getUpperLimitOperator().orElse(null); + AirspaceVolumeType avt = create(AirspaceVolumeType.class); + avt.setId(getUUID()); + if (lowerLevel != null) { + if (upperLevel != null) { + //BTW BTW_SFC + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + if (lowerLevel.getValue() == 0) { + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("SFC"); + })); + } else { + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else if (lowerLimitOperator == null) { + //AT + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + avt.setUpperLimit(toValDistanceVertical(lowerLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else { + //ABV + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } + } else { + if (upperLevel == null) { + // In this case no levels are specified + } else { + if (upperLimitOperator == null) { + //TOP + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else if (AviationCodeListUser.RelationalOperator.ABOVE.equals(upperLimitOperator)) { + // TOP ABV + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + avt.setMaximumLimit(create(ValDistanceVerticalType.class, vdvt -> { + vdvt.setNilReason("unknown"); + })); + } else if (AviationCodeListUser.RelationalOperator.BELOW.equals(upperLimitOperator)) { + // TOP BLW + avt.setUpperLimit(create(ValDistanceVerticalType.class, vdvt -> { + vdvt.setNilReason("unknown"); + })); + avt.setMaximumLimit(toValDistanceVertical(upperLevel).get()); + avt.setMaximumLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + + } + } + } + an.getGeometry().flatMap(TacOrGeoGeometry::getGeoGeometry) + .ifPresent(geom -> avt.setHorizontalProjection(createAixm511fullSurface(geom, getUUID()))); + + return avt; + } + + + protected void updateMessageMetadata(final AIRMET source, final ConversionResult results, final AIRMETType target) throws ConversionException { + try { + final DatatypeFactory f = DatatypeFactory.newInstance(); + + if (source.getPermissibleUsage().isPresent()) { + switch (source.getPermissibleUsage().get()) { + case NON_OPERATIONAL: + target.setPermissibleUsage(PermissibleUsageType.NON_OPERATIONAL); + source.getPermissibleUsageReason().ifPresent(r -> { + target.setPermissibleUsageReason(PermissibleUsageReasonType.valueOf(source.getPermissibleUsageReason().get().name())); + source.getPermissibleUsageSupplementary().ifPresent(target::setPermissibleUsageSupplementary); + }); + break; + case OPERATIONAL: + target.setPermissibleUsage(PermissibleUsageType.OPERATIONAL); + break; + } + } else { + // Default permissions + target.setPermissibleUsage(PermissibleUsageType.NON_OPERATIONAL); + target.setPermissibleUsageReason(PermissibleUsageReasonType.TEST); + } + + if (source.isTranslated()) { + if (source.getTranslatedBulletinID().isPresent()) { + target.setTranslatedBulletinID(source.getTranslatedBulletinID().get()); + } + source.getTranslatedBulletinReceptionTime() + .map(time -> f.newXMLGregorianCalendar(toIWXXMDateTime(time))) + .ifPresent(target::setTranslatedBulletinReceptionTime); + source.getTranslationCentreDesignator().ifPresent(target::setTranslationCentreDesignator); + source.getTranslationCentreName().ifPresent(target::setTranslationCentreName); + source.getTranslationTime().map(time -> f.newXMLGregorianCalendar(toIWXXMDateTime(time))).ifPresent(target::setTranslationTime); + if (results.getStatus() != Status.SUCCESS) { + source.getTranslatedTAC().ifPresent(target::setTranslationFailedTAC); + } + } + } catch (final DatatypeConfigurationException e) { + throw new ConversionException("Exception in setting the translation time", e); + } + + } + + @Override + protected InputStream getCleanupTransformationStylesheet(final ConversionHints hints) throws ConversionException { + final InputStream retval = this.getClass().getResourceAsStream("AIRMETCleanup.xsl"); + if (retval == null) { + throw new ConversionException("Error accessing cleanup XSLT sheet file"); + } + return retval; + } + + public static class ToDOM extends AIRMETIWXXMSerializer { + @Override + protected Document render(final AIRMETType airmet, final ConversionHints hints) throws ConversionException { + return this.renderXMLDocument(airmet, hints); + } + + @Override + protected IssueList validate(final Document output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) throws ConversionException { + return AIRMETIWXXMSerializer.validateDOMAgainstSchemaAndSchematron(output, schemaInfo, hints); + } + } + + public static class ToString extends AIRMETIWXXMSerializer { + + @Override + protected String render(final AIRMETType airmet, final ConversionHints hints) throws ConversionException { + final Document result = renderXMLDocument(airmet, hints); + return renderDOMToString(result, hints); + } + + @Override + protected IssueList validate(final String output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) throws ConversionException { + return AIRMETIWXXMSerializer.validateStringAgainstSchemaAndSchematron(output, schemaInfo, hints); + } + } +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETIWXXMSerializer.java new file mode 100644 index 00000000..b4c6824d --- /dev/null +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETIWXXMSerializer.java @@ -0,0 +1,645 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1.sigmet; + +import aero.aixm511full.ValDistanceVerticalType; +import aero.aixm511full.*; +import fi.fmi.avi.converter.*; +import fi.fmi.avi.converter.ConversionResult.Status; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.ReferredObjectRetrievalContext; +import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; +import fi.fmi.avi.converter.iwxxm.v2023_1.AbstractIWXXM20231Serializer; +import fi.fmi.avi.model.*; +import fi.fmi.avi.model.AviationCodeListUser.AeronauticalSignificantWeatherPhenomenon; +import fi.fmi.avi.model.sigmet.SIGMET; +import fi.fmi.avi.model.sigmet.SigmetAnalysisType; +import fi.fmi.avi.model.sigmet.VAInfo; +import icao.iwxxm2023_1.AirspacePropertyType; +import icao.iwxxm2023_1.AirspaceVolumePropertyType; +import icao.iwxxm2023_1.UnitPropertyType; +import icao.iwxxm2023_1.*; +import net.opengis.gml32.PointPropertyType; +import net.opengis.gml32.PointType; +import net.opengis.gml32.*; +import net.opengis.om20.TimeObjectPropertyType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import wmo.metce2013.VolcanoPropertyType; +import wmo.metce2013.VolcanoType; + +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.InputStream; +import java.math.BigInteger; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +public abstract class SIGMETIWXXMSerializer extends AbstractIWXXM20231Serializer { + private static final Logger LOG = LoggerFactory.getLogger(SIGMETIWXXMSerializer.class); + + @SuppressWarnings("unchecked") + private SIGMETPositionCollectionType createFPA(final List fcs, + final String fcTime, final boolean noVaExp) { + + AbstractTimeObjectPropertyType phenTimeProp; + if (fcTime != null) { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> { + final JAXBElement wrapped = createAndWrap(TimeInstantType.class, period -> { + period.setId(getUUID()); + period.setTimePosition(create(TimePositionType.class, tPos -> tPos.getValue().add(fcTime))); + }); + toProp.setAbstractTimeObject((JAXBElement) wrapped); + }); + } else { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> toProp.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_MISSING)); + } + SIGMETPositionCollectionType spc = create(SIGMETPositionCollectionType.class, spct -> { + spct.setPhenomenonTime(phenTimeProp); + spct.setId(getUUID()); + + if (noVaExp) { + spct.getMember().add(create(SIGMETPositionPropertyType.class, sppt -> { + sppt.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_NOTHING_OF_OPERATIONAL_SIGNIFICANCE); + })); + } else { + for (PhenomenonGeometry fc : fcs) { + LOG.info("adding forecast geometry"); + spct.getMember().add(create(SIGMETPositionPropertyType.class, sppt -> { + sppt.setSIGMETPosition(create(SIGMETPositionType.class, spt -> { + spt.setApproximateLocation(false); + spt.setId(getUUID()); + spt.setGeometry(null); + spt.setGeometry(create(AirspaceVolumePropertyType.class, avpt -> { + avpt.setAirspaceVolume(createAirspaceVolume(fc)); + })); + })); + + })); + } + } + + }); + return spc; + } + + protected static TimePeriodPropertyType getTimePeriodPropertyType(final SIGMET input) { + return getATimePeriodPropertyType(input.getValidityPeriod()); + } + + protected static Optional getCancelledTimePeriodPropertyType(final SIGMET input) { + return input.getCancelledReference() + .map(airmetReference -> getATimePeriodPropertyType(airmetReference.getValidityPeriod())); + } + + protected static TimePeriodPropertyType getATimePeriodPropertyType(final PartialOrCompleteTimePeriod valTime) { + return create(TimePeriodPropertyType.class, prop -> { + final TimePeriodType tp = create(TimePeriodType.class); + tp.setId(getUUID()); + final TimePositionType beginPos = create(TimePositionType.class); + startToIWXXMDateTime(valTime).ifPresent(time -> beginPos.getValue().add(time)); + tp.setBeginPosition(beginPos); + final TimePositionType endPos = create(TimePositionType.class); + endToIWXXMDateTime(valTime).ifPresent(time -> endPos.getValue().add(time)); + tp.setEndPosition(endPos); + prop.setTimePeriod(tp); + }); + } + + public static Optional getCompleteTimePeriod( + final TimeObjectPropertyType timeObjectPropertyType, final ReferredObjectRetrievalContext refCtx) { + final Optional to = resolveProperty(timeObjectPropertyType, "abstractTimeObject", + AbstractTimeObjectType.class, refCtx); + if (to.isPresent()) { + if (TimePeriodType.class.isAssignableFrom(to.get().getClass())) { + final TimePeriodType tp = (TimePeriodType) to.get(); + final PartialOrCompleteTimePeriod.Builder retval = PartialOrCompleteTimePeriod.builder(); + getStartTime(tp, refCtx).ifPresent(start -> retval.setStartTime(PartialOrCompleteTimeInstant.builder()// + .setCompleteTime(start).build())); + + getEndTime(tp, refCtx).ifPresent(end -> retval.setEndTime(PartialOrCompleteTimeInstant.builder()// + .setCompleteTime(end).build())); + return Optional.of(retval.build()); + } + } + return Optional.empty(); + } + + public static Optional getCompleteTimeInstant( + final TimeObjectPropertyType timeObjectPropertyType, final ReferredObjectRetrievalContext refCtx) { + final Optional to = resolveProperty(timeObjectPropertyType, "abstractTimeObject", + AbstractTimeObjectType.class, refCtx); + if (to.isPresent()) { + if (TimeInstantType.class.isAssignableFrom(to.get().getClass())) { + final TimeInstantType ti = (TimeInstantType) to.get(); + final Optional time = getTime(ti.getTimePosition()); + if (time.isPresent()) { + return Optional.of(PartialOrCompleteTimeInstant.builder().setCompleteTime(time).build()); + } + } else { + throw new IllegalArgumentException("Time object is not a time instant"); + } + } + return Optional.empty(); + } + + protected abstract T render(final SIGMETType sigmet, final ConversionHints hints) throws ConversionException; + + protected abstract IssueList validate(final T output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) + throws ConversionException; + + /** + * Converts a SIGMET object into another format. + * + * @param input input message + * @param hints parsing hints + * @return the conversion result. + */ + @Override + public ConversionResult convertMessage(final SIGMET input, final ConversionHints hints) { + final ConversionResult result = new ConversionResult<>(); + + LOG.info("Starting conversion of SIGMET"); + if (!input.areAllTimeReferencesComplete()) { + result.addIssue(new ConversionIssue(ConversionIssue.Type.MISSING_DATA, + "All time references must be completed before converting to IWXXM")); + return result; + } + if (!input.getPhenomenon().isPresent()) { + result.addIssue(new ConversionIssue(ConversionIssue.Type.MISSING_DATA, + "A SIGMET phenomenon has to be specified")); + return result; + } + + final SIGMETType sigmet; + AeronauticalSignificantWeatherPhenomenon sigmetPhenomen = input.getPhenomenon().get(); + switch (sigmetPhenomen) { + case TC: + sigmet = create(TropicalCycloneSIGMETType.class); + sigmet.setId(getUUID()); + break; + case VA: + sigmet = create(VolcanicAshSIGMETType.class); + sigmet.setId(getUUID()); + break; + default: + sigmet = create(SIGMETType.class); + sigmet.setId(getUUID()); + } + + if (input.getCancelledReference().isPresent()) { + sigmet.setId(getUUID()); + sigmet.setReportStatus(ReportStatusType.NORMAL); + sigmet.setIsCancelReport(true); + sigmet.setCancelledReportSequenceNumber(input.getCancelledReference().get().getSequenceNumber()); + sigmet.setPhenomenon(null); + getCancelledTimePeriodPropertyType(input) + .ifPresent(sigmet::setCancelledReportValidPeriod); + if (sigmetPhenomen.equals(AviationCodeListUser.AeronauticalSignificantWeatherPhenomenon.VA)) { + input.getVAInfo().flatMap(VAInfo::getVolcanicAshMovedToFIR).ifPresent(movedToFir -> { + ((VolcanicAshSIGMETType) sigmet).setVolcanicAshMovedToFIR( + create(AirspacePropertyType.class, prop -> prop.setAirspace(create(AirspaceType.class, airspace -> { + airspace.setValidTime(null); + airspace.setId(getUUID()); + airspace.getTimeSlice() + .add(create(AirspaceTimeSlicePropertyType.class, timeSliceProp -> timeSliceProp + .setAirspaceTimeSlice(create(AirspaceTimeSliceType.class, timeSlice -> { + timeSlice.setValidTime(create(TimePrimitivePropertyType.class)); + timeSlice.setInterpretation("SNAPSHOT"); + timeSlice.setType(create(CodeAirspaceType.class, type -> type.setValue(movedToFir.getType()))); + timeSlice.setAirspaceName(create(TextNameType.class, name -> name + .setValue(movedToFir.getName()))); + timeSlice.setId(getUUID()); + timeSlice.setDesignator(create(CodeAirspaceDesignatorType.class, desig -> desig + .setValue(movedToFir.getDesignator()))); + })))); + })))); + }); + } + } else { + sigmet.setReportStatus(ReportStatusType.NORMAL); + sigmet.setIsCancelReport(false); + sigmet.setPhenomenon(create(AeronauticalSignificantWeatherPhenomenonType.class, phen -> phen.setHref( + AviationCodeListUser.CODELIST_SIGWX_PHENOMENA_ROOT + sigmetPhenomen.name()))); + } + + // Use current time as issueTime if missing + final String issueTime = input.getIssueTime().flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .orElseGet(() -> toIWXXMDateTime(ZonedDateTime.now())); + + sigmet.setIssueTime( + create(TimeInstantPropertyType.class, tip -> tip.setTimeInstant(create(TimeInstantType.class, ti -> { + ti.setTimePosition(create(TimePositionType.class, tp -> tp.getValue().add(issueTime))); + ti.setId(getUUID()); + })))); + + sigmet.setIssuingAirTrafficServicesUnit( + create(UnitPropertyType.class, prop -> prop.setUnit(create(UnitType.class, unit -> { + unit.setId(getUUID()); + unit.getTimeSlice().add(create(UnitTimeSlicePropertyType.class, + sliceProp -> sliceProp.setUnitTimeSlice(create(UnitTimeSliceType.class, slice -> { + slice.setId(getUUID()); + slice.setValidTime(create(TimePrimitivePropertyType.class, tp -> { + })); + slice.setInterpretation("SNAPSHOT"); + slice.setType(create(CodeUnitType.class, codeUnitType -> codeUnitType.setValue("FIC"))); + slice.setUnitName(create(TextNameType.class, tnt -> { + tnt.setValue(input.getIssuingAirTrafficServicesUnit().getName()); + })); + slice.setDesignator(create(CodeOrganisationDesignatorType.class, desig -> + desig.setValue(input.getIssuingAirTrafficServicesUnit().getDesignator()))); + })))); + })))); + + sigmet.setOriginatingMeteorologicalWatchOffice( + create(UnitPropertyType.class, prop -> prop.setUnit(create(UnitType.class, unit -> { + unit.setId(getUUID()); + unit.getTimeSlice().add(create(UnitTimeSlicePropertyType.class, + sliceProp -> sliceProp.setUnitTimeSlice(create(UnitTimeSliceType.class, slice -> { + slice.setId(getUUID()); + slice.setValidTime(create(TimePrimitivePropertyType.class, tp -> { + })); + slice.setInterpretation("SNAPSHOT"); + slice.setType(create(CodeUnitType.class, codeUnitType -> codeUnitType.setValue("MWO"))); + slice.setUnitName(create(TextNameType.class, tnt -> { + tnt.setValue(input.getMeteorologicalWatchOffice().getName()); + })); + + slice.setDesignator(create(CodeOrganisationDesignatorType.class, desig -> + desig.setValue(input.getMeteorologicalWatchOffice().getDesignator()))); + })))); + })))); + + sigmet.setIssuingAirTrafficServicesRegion( + create(AirspacePropertyType.class, prop -> prop.setAirspace(create(AirspaceType.class, airspace -> { + airspace.setValidTime(null); + airspace.setId(getUUID()); + airspace.getTimeSlice() + .add(create(AirspaceTimeSlicePropertyType.class, timeSliceProp -> timeSliceProp + .setAirspaceTimeSlice(create(AirspaceTimeSliceType.class, timeSlice -> { + timeSlice.setValidTime(create(TimePrimitivePropertyType.class)); + timeSlice.setInterpretation("SNAPSHOT"); + timeSlice.setType(create(CodeAirspaceType.class, type -> type.setValue("FIR"))); + timeSlice.setAirspaceName(create(TextNameType.class, name -> name + .setValue(input.getAirspace().getName()))); + timeSlice.setId(getUUID()); + timeSlice.setDesignator(create(CodeAirspaceDesignatorType.class, desig -> desig + .setValue(input.getAirspace().getDesignator()))); + + })))); + + })))); + + sigmet.setSequenceNumber( + create(StringWithNilReasonType.class, prop -> prop.setValue(input.getSequenceNumber()))); + + sigmet.setValidPeriod(getTimePeriodPropertyType(input)); + ZonedDateTime startTime = input.getValidityPeriod().getStartTime().get().getCompleteTime().get(); + + if (!input.getCancelledReference().isPresent()) { + final String analysisTime = input.getAnalysisGeometries()// + .map(AbstractIWXXM20231Serializer::getFirstOrNull)// + .flatMap(PhenomenonGeometryWithHeight::getTime)// + .flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .orElse(null); + boolean noForecasts = !input.getForecastGeometries().isPresent() || (input.getForecastGeometries().get().isEmpty()); + + List ans = input.getAnalysisGeometries().get(); + + final AnalysisAndForecastPositionAnalysisType analysis = create(AnalysisAndForecastPositionAnalysisType.class, a -> a.setId(getUUID())); + final SIGMETEvolvingConditionCollectionType secct = createAnalysis(ans, analysisTime, startTime, noForecasts); + analysis.setAnalysis(create(SIGMETEvolvingConditionCollectionPropertyType.class, s -> s.setSIGMETEvolvingConditionCollection(secct))); + + if (input.getForecastGeometries().isPresent()) { + final String fcTime = input.getForecastGeometries()// + .map(AbstractIWXXM20231Serializer::getFirstOrNull)// + .flatMap(PhenomenonGeometry::getTime)// + .flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// + .orElse(null); + + boolean noVaExp = false; + if (input.getVAInfo().isPresent()) { + noVaExp = input.getForecastGeometries().get().get(0).getNoVolcanicAshExpected().orElse(false); + } + + SIGMETPositionCollectionType fpa = createFPA(input.getForecastGeometries().get(), fcTime, noVaExp); + analysis.setForecastPositionAnalysis(create(SIGMETPositionCollectionPropertyType.class, s -> s.setSIGMETPositionCollection(fpa))); + } + + sigmet.getAnalysisCollection().add(create(SIGMETType.AnalysisCollection.class, ac -> ac.setAnalysisAndForecastPositionAnalysis(analysis))); + + if (sigmetPhenomen.equals(AviationCodeListUser.AeronauticalSignificantWeatherPhenomenon.VA) + && input.getVAInfo().isPresent() + && input.getVAInfo().get().getVolcano().isPresent()) { + final VolcanoDescription volcano = input.getVAInfo().get().getVolcano().get(); + + ((VolcanicAshSIGMETType) sigmet).setEruptingVolcano(create(VolcanoPropertyType.class, vpt -> { + vpt.setVolcano(createAndWrap(VolcanoType.class, v -> { + v.setId(getUUID()); + if (volcano.getVolcanoPosition().isPresent()) { + final Double[] pts = volcano.getVolcanoPosition().get().getCoordinates().toArray(new Double[0]); + v.setPosition(create(PointPropertyType.class, ppt -> ppt.setPoint(create(PointType.class, pt -> { + pt.setPos(create(DirectPositionType.class, dpt -> { + dpt.getValue().addAll(Arrays.asList(pts)); + dpt.setSrsName(AviationCodeListUser.CODELIST_VALUE_EPSG_4326); + dpt.setSrsDimension(BigInteger.valueOf(2)); + dpt.getAxisLabels().add("Lat"); + dpt.getAxisLabels().add("Lon"); + dpt.getUomLabels().add("deg"); + dpt.getUomLabels().add("deg"); + })); + pt.setId(getUUID()); + })))); + } else { + v.setPosition(create(PointPropertyType.class, ppt -> ppt.getNilReason().add("http://codes.wmo.int/common/nil/unknown"))); + } + if (volcano.getVolcanoName().isPresent()) { + v.setVolcanoName(volcano.getVolcanoName().get()); + } else { + final String generatedVolcanoName = "Unknown"; + v.setVolcanoName(generatedVolcanoName); + } + })); + })); + } + } + try { + this.updateMessageMetadata(input, result, sigmet); + final T rendered = this.render(sigmet, hints); + result.addIssue(validate(rendered, getSchemaInfo(), hints)); + result.setConvertedMessage(rendered); + } catch (final ConversionException e) { + result.setStatus(Status.FAIL); + result.addIssue(new ConversionIssue(ConversionIssue.Type.OTHER, + "Unable to render SIGMET IWXXM message to String" + " " + e.getCause(), e)); + } + return result; + } + + @SuppressWarnings("unchecked") + private SIGMETEvolvingConditionCollectionType createAnalysis( + final List ans, final String analysisTime, final ZonedDateTime validityStart, + final boolean noForecasts) { + + AbstractTimeObjectPropertyType phenTimeProp; + if (analysisTime != null) { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> { + final JAXBElement wrapped = createAndWrap(TimeInstantType.class, inst -> { + inst.setId(getUUID()); + ZonedDateTime phenTime = ZonedDateTime.parse(analysisTime, DateTimeFormatter.ISO_INSTANT.withZone(ZoneOffset.UTC)); + if (phenTime.isEqual(validityStart)) { + inst.setTimePosition(create(TimePositionType.class, tPos -> tPos.getValue().add(analysisTime))); + } else { + String phenomenonTimeString = validityStart.format(DateTimeFormatter.ISO_INSTANT); + inst.setTimePosition(create(TimePositionType.class, tPos -> tPos.getValue().add(phenomenonTimeString))); + } + }); + toProp.setAbstractTimeObject((JAXBElement) wrapped); + }); + } else { + phenTimeProp = create(AbstractTimeObjectPropertyType.class, toProp -> toProp.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_MISSING)); + } + SIGMETEvolvingConditionCollectionType sigmetEvolvingConditionCollectionType = create( + SIGMETEvolvingConditionCollectionType.class, ecct -> { + ecct.setPhenomenonTime(phenTimeProp); + ecct.setId(getUUID()); + for (PhenomenonGeometryWithHeight an : ans) { + if (an.getAnalysisType().equals(SigmetAnalysisType.FORECAST)) { + ecct.setTimeIndicator(TimeIndicatorType.FORECAST); + } else if (an.getAnalysisType().equals(SigmetAnalysisType.OBSERVATION)) { + ecct.setTimeIndicator(TimeIndicatorType.OBSERVATION); + } else { + ecct.setTimeIndicator(null); + } + ecct.getMember().add(create(SIGMETEvolvingConditionPropertyType.class, seccpt -> { + seccpt.setSIGMETEvolvingCondition(create(SIGMETEvolvingConditionType.class, sect -> { + if (an.getIntensityChange().isPresent()) { + switch (an.getIntensityChange().get()) { + case INTENSIFYING: + sect.setIntensityChange(SIGMETExpectedIntensityChangeType.INTENSIFY); + break; + case WEAKENING: + sect.setIntensityChange(SIGMETExpectedIntensityChangeType.WEAKEN); + break; + case NO_CHANGE: + sect.setIntensityChange(SIGMETExpectedIntensityChangeType.NO_CHANGE); + break; + } + } else { + sect.setIntensityChange(SIGMETExpectedIntensityChangeType.NO_CHANGE); + } + sect.setId(getUUID()); + sect.setGeometry(create(AirspaceVolumePropertyType.class, avpt -> { + avpt.setAirspaceVolume(createAirspaceVolume(an)); + })); + if (noForecasts) { // Only add Motion elements if there are no forecasts + if (an.getMovingDirection().isPresent()) { + final AngleWithNilReasonType angl = new AngleWithNilReasonType(); + final NumericMeasure md = an.getMovingDirection().get(); + angl.setUom(md.getUom()); + angl.setValue(md.getValue()); + sect.setDirectionOfMotion(angl); + + an.getMovingSpeed() + .ifPresent(ms -> sect.setSpeedOfMotion(create(SpeedType.class, spd -> { + if (ms.getUom().equals("KT")) { + spd.setUom("[kn_i]"); + } else if (ms.getUom().equals("KMH")) { + spd.setUom("km/h"); + } else { + spd.setUom(ms.getUom()); + } + spd.setValue(ms.getValue()); + }))); + } else { + sect.setDirectionOfMotion(create(AngleWithNilReasonType.class, angle -> { + angle.getNilReason().add(AviationCodeListUser.CODELIST_VALUE_NIL_REASON_INAPPLICABLE); + angle.setUom("deg"); + })); + sect.setSpeedOfMotion(create(SpeedType.class, speed -> { + speed.setValue(0); + speed.setUom("[kn_i]"); + })); + } + } + })); + })); + } + }); + + return sigmetEvolvingConditionCollectionType; + } + + private AirspaceVolumeType createAirspaceVolume(PhenomenonGeometryWithHeight an) { + NumericMeasure lowerLevel = an.getLowerLimit().orElse(null); + NumericMeasure upperLevel = an.getUpperLimit().orElse(null); + AviationCodeListUser.RelationalOperator lowerLimitOperator = an.getLowerLimitOperator().orElse(null); + AviationCodeListUser.RelationalOperator upperLimitOperator = an.getUpperLimitOperator().orElse(null); + AirspaceVolumeType avt = create(AirspaceVolumeType.class); + avt.setId(getUUID()); + if (lowerLevel != null) { + if (upperLevel != null) { + //BTW BTW_SFC + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + if (lowerLevel.getValue() == 0) { + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("SFC"); + })); + } else { + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else if (lowerLimitOperator == null) { + //AT + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + avt.setUpperLimit(toValDistanceVertical(lowerLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else { + //ABV + avt.setLowerLimit(toValDistanceVertical(lowerLevel).get()); + avt.setLowerLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } + } else { + if (upperLevel == null) { + // In this case no levels are specified + } else { + if (upperLimitOperator == null) { + //TOP + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + } else if (AviationCodeListUser.RelationalOperator.ABOVE.equals(upperLimitOperator)) { + // TOP ABV + avt.setUpperLimit(toValDistanceVertical(upperLevel).get()); + avt.setUpperLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + avt.setMaximumLimit(create(ValDistanceVerticalType.class, vdvt -> { + vdvt.setNilReason("unknown"); + })); + } else if (AviationCodeListUser.RelationalOperator.BELOW.equals(upperLimitOperator)) { + // TOP BLW + avt.setUpperLimit(create(ValDistanceVerticalType.class, vdvt -> { + vdvt.setNilReason("unknown"); + })); + avt.setMaximumLimit(toValDistanceVertical(upperLevel).get()); + avt.setMaximumLimitReference(create(CodeVerticalReferenceType.class, cvrt -> { + cvrt.setValue("STD"); + })); + + } + } + } + an.getGeometry().flatMap(TacOrGeoGeometry::getGeoGeometry) + .ifPresent(geom -> avt.setHorizontalProjection(createAixm511fullSurface(geom, getUUID()))); + + return avt; + } + + private AirspaceVolumeType createAirspaceVolume(PhenomenonGeometry fc) { + AirspaceVolumeType airspace = create(AirspaceVolumeType.class, avt -> { + avt.setId(getUUID()); + fc.getGeometry().flatMap(TacOrGeoGeometry::getGeoGeometry) + .ifPresent(geom -> avt.setHorizontalProjection(createAixm511fullSurface(geom, getUUID()))); + + }); + return airspace; + } + + protected void updateMessageMetadata(final SIGMET source, final ConversionResult results, + final SIGMETType target) throws ConversionException { + try { + final DatatypeFactory f = DatatypeFactory.newInstance(); + + if (source.getPermissibleUsage().isPresent()) { + switch (source.getPermissibleUsage().get()) { + case NON_OPERATIONAL: + target.setPermissibleUsage(PermissibleUsageType.NON_OPERATIONAL); + source.getPermissibleUsageReason().ifPresent(r -> { + target.setPermissibleUsageReason(PermissibleUsageReasonType.valueOf(source.getPermissibleUsageReason().get().name())); + source.getPermissibleUsageSupplementary().ifPresent(target::setPermissibleUsageSupplementary); + }); + break; + case OPERATIONAL: + target.setPermissibleUsage(PermissibleUsageType.OPERATIONAL); + break; + } + } else { + // Default permissions + target.setPermissibleUsage(PermissibleUsageType.NON_OPERATIONAL); + target.setPermissibleUsageReason(PermissibleUsageReasonType.TEST); + } + ; + + if (source.isTranslated()) { + source.getTranslatedBulletinID().ifPresent(target::setTranslatedBulletinID); + source.getTranslatedBulletinReceptionTime()// + .map(time -> f.newXMLGregorianCalendar(toIWXXMDateTime(time)))// + .ifPresent(target::setTranslatedBulletinReceptionTime); + source.getTranslationCentreDesignator().ifPresent(target::setTranslationCentreDesignator); + source.getTranslationCentreName().ifPresent(target::setTranslationCentreName); + source.getTranslationTime()// + .map(time -> f.newXMLGregorianCalendar(toIWXXMDateTime(time)))// + .ifPresent(target::setTranslationTime); + if (results.getStatus() != Status.SUCCESS) { + source.getTranslatedTAC().ifPresent(target::setTranslationFailedTAC); + } + } + } catch (final DatatypeConfigurationException e) { + throw new ConversionException("Exception in setting the translation time", e); + } + } + + @Override + protected InputStream getCleanupTransformationStylesheet(final ConversionHints hints) throws ConversionException { + final InputStream retval = this.getClass().getResourceAsStream("SIGMETCleanup.xsl"); + if (retval == null) { + throw new ConversionException("Error accessing cleanup XSLT sheet file"); + } + return retval; + } + + public static class ToDOM extends SIGMETIWXXMSerializer { + @Override + protected Document render(final SIGMETType sigmet, final ConversionHints hints) throws ConversionException { + return renderXMLDocument(sigmet, hints); + } + + @Override + protected IssueList validate(final Document output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) + throws ConversionException { + return SIGMETIWXXMSerializer.validateDOMAgainstSchemaAndSchematron(output, schemaInfo, hints); + } + } + + public static class ToString extends SIGMETIWXXMSerializer { + @Override + protected String render(final SIGMETType sigmet, final ConversionHints hints) throws ConversionException { + final Document result = renderXMLDocument(sigmet, hints); + return renderDOMToString(result, hints); + } + + @Override + protected IssueList validate(final String output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) + throws ConversionException { + return SIGMETIWXXMSerializer.validateStringAgainstSchemaAndSchematron(output, schemaInfo, hints); + } + } +} diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Parser.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Parser.java index 0d2f5160..5ad409d3 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Parser.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Parser.java @@ -1,16 +1,19 @@ package fi.fmi.avi.converter.iwxxm.v2_1; -import fi.fmi.avi.converter.iwxxm.AbstractIWXXMParser; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMAixm511WxParser; +import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolverAixm511Wx; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; import fi.fmi.avi.model.AviationWeatherMessageOrCollection; -public abstract class AbstractIWXXM21Parser extends AbstractIWXXMParser { + +public abstract class AbstractIWXXM21Parser extends AbstractIWXXMAixm511WxParser { @Override protected XMLSchemaInfo getSchemaInfo() { - final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(F_SECURE_PROCESSING); + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Wx.getInstance(), F_SECURE_PROCESSING); schemaInfo.addSchemaSource(icao.iwxxm21.ReportType.class.getResource("/int/icao/iwxxm/2.1.1/iwxxm.xsd")); schemaInfo.addSchematronRule(icao.iwxxm21.ReportType.class.getResource("/schematron/xslt/int/icao/iwxxm/2.1.1/rule/iwxxm.xsl")); return schemaInfo; } + } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Serializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Serializer.java index 20f73735..69a9f4e6 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Serializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/AbstractIWXXM21Serializer.java @@ -1,5 +1,18 @@ package fi.fmi.avi.converter.iwxxm.v2_1; +import aero.aixm511.AirportHeliportType; +import fi.fmi.avi.converter.ConversionResult; +import fi.fmi.avi.converter.iwxxm.*; +import fi.fmi.avi.model.*; +import icao.iwxxm21.*; +import net.opengis.gml32.*; +import net.opengis.om20.OMObservationType; +import net.opengis.om20.TimeObjectPropertyType; +import net.opengis.sampling.spatial.SFSpatialSamplingFeatureType; +import net.opengis.sampling.spatial.ShapeType; + +import javax.xml.bind.JAXBElement; +import javax.xml.namespace.QName; import java.math.BigInteger; import java.time.ZonedDateTime; import java.util.Collections; @@ -7,45 +20,7 @@ import java.util.Optional; import java.util.UUID; -import javax.xml.bind.JAXBElement; -import javax.xml.namespace.QName; - -import net.opengis.gml32.AbstractGeometryType; -import net.opengis.gml32.AbstractTimeObjectType; -import net.opengis.gml32.DirectPositionType; -import net.opengis.gml32.FeaturePropertyType; -import net.opengis.gml32.PointType; -import net.opengis.gml32.ReferenceType; -import net.opengis.gml32.TimeInstantType; -import net.opengis.gml32.TimePeriodType; -import net.opengis.om20.OMObservationType; -import net.opengis.om20.TimeObjectPropertyType; -import net.opengis.sampling.spatial.SFSpatialSamplingFeatureType; -import net.opengis.sampling.spatial.ShapeType; - -import aero.aixm511.AirportHeliportType; -import fi.fmi.avi.converter.ConversionResult; -import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; -import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; -import fi.fmi.avi.converter.iwxxm.ReferredObjectRetrievalContext; -import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; -import fi.fmi.avi.model.Aerodrome; -import fi.fmi.avi.model.AviationCodeListUser; -import fi.fmi.avi.model.AviationWeatherMessageOrCollection; -import fi.fmi.avi.model.CloudForecast; -import fi.fmi.avi.model.CloudLayer; -import fi.fmi.avi.model.ElevatedPoint; -import fi.fmi.avi.model.PartialOrCompleteTimeInstant; -import fi.fmi.avi.model.PartialOrCompleteTimePeriod; -import icao.iwxxm21.AerodromeCloudForecastType; -import icao.iwxxm21.CloudAmountReportedAtAerodromeType; -import icao.iwxxm21.CloudLayerType; -import icao.iwxxm21.DistanceWithNilReasonType; -import icao.iwxxm21.LengthWithNilReasonType; -import icao.iwxxm21.SigConvectiveCloudTypeType; -import icao.iwxxm21.TAFType; - -public abstract class AbstractIWXXM21Serializer extends AbstractIWXXMSerializer { +public abstract class AbstractIWXXM21Serializer extends AbstractIWXXMAixm511WxSerializer { private static IWXXMNamespaceContext nsCtx; @@ -97,7 +72,7 @@ protected static E getFirstOrNull(final List list) { @Override public XMLSchemaInfo getSchemaInfo() { - final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(F_SECURE_PROCESSING); + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Wx.getInstance(), F_SECURE_PROCESSING); schemaInfo.addSchemaSource(TAFType.class.getResource("/int/icao/iwxxm/2.1.1/iwxxm.xsd")); schemaInfo.addSchematronRule(TAFType.class.getResource("/schematron/xslt/int/icao/iwxxm/2.1.1/rule/iwxxm.xsl")); schemaInfo.addSchemaLocation("http://icao.int/iwxxm/2.1", "https://schemas.wmo.int/iwxxm/2.1.1/iwxxm.xsd"); @@ -141,7 +116,7 @@ protected void updateSamplingFeature(final Aerodrome input, final OMObservationT final JAXBElement wrapped = wrap(create(PointType.class, point -> { final Optional inputPos = input.getReferencePoint(); if (inputPos.isPresent()) { - point.setId("point-" + UUID.randomUUID().toString()); + point.setId("point-" + UUID.randomUUID()); inputPos.get().getCrs().ifPresent(crs -> setCrsToType(point, crs)); if (inputPos.get().getCoordinates() != null) { point.setSrsDimension(BigInteger.valueOf(inputPos.get().getCoordinates().size())); @@ -168,7 +143,7 @@ protected void updateSamplingFeature(final Aerodrome input, final OMObservationT protected void updateForecastClouds(final CloudForecast source, final AerodromeCloudForecastType target, final ConversionResult result) { if (source != null) { - target.setId("cfct-" + UUID.randomUUID().toString()); + target.setId("cfct-" + UUID.randomUUID()); source.getVerticalVisibility().ifPresent(measure -> { final LengthWithNilReasonType vvValue = asMeasure(measure, LengthWithNilReasonType.class); final QName eName = new QName(IWXXMNamespaceContext.getDefaultURI("iwxxm"), "verticalVisibility"); diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/taf/TAFIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/taf/TAFIWXXMSerializer.java index 6f1f7fa7..2bdc0031 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/taf/TAFIWXXMSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v2_1/taf/TAFIWXXMSerializer.java @@ -1,85 +1,40 @@ package fi.fmi.avi.converter.iwxxm.v2_1.taf; -import static fi.fmi.avi.model.AviationCodeListUser.CODELIST_VALUE_NIL_REASON_NOTHING_OF_OPERATIONAL_SIGNIFICANCE; - -import java.io.InputStream; -import java.time.ZonedDateTime; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.UUID; - -import javax.xml.bind.JAXBElement; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; - -import net.opengis.gml32.AbstractTimeObjectType; -import net.opengis.gml32.AngleType; -import net.opengis.gml32.FeaturePropertyType; -import net.opengis.gml32.LengthType; -import net.opengis.gml32.MeasureType; -import net.opengis.gml32.ReferenceType; -import net.opengis.gml32.SpeedType; -import net.opengis.gml32.StringOrRefType; -import net.opengis.gml32.TimeInstantPropertyType; -import net.opengis.gml32.TimeInstantType; -import net.opengis.gml32.TimePeriodPropertyType; -import net.opengis.gml32.TimePeriodType; -import net.opengis.gml32.TimePositionType; -import net.opengis.om20.OMObservationPropertyType; -import net.opengis.om20.OMObservationType; -import net.opengis.om20.OMProcessPropertyType; -import net.opengis.om20.TimeObjectPropertyType; - -import org.w3c.dom.Document; - import aero.aixm511.AirportHeliportType; -import fi.fmi.avi.converter.ConversionException; -import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.*; import fi.fmi.avi.converter.ConversionIssue.Type; -import fi.fmi.avi.converter.ConversionResult; import fi.fmi.avi.converter.ConversionResult.Status; -import fi.fmi.avi.converter.IssueList; import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; import fi.fmi.avi.converter.iwxxm.v2_1.AbstractIWXXM21Serializer; -import fi.fmi.avi.model.AviationCodeListUser; +import fi.fmi.avi.model.*; import fi.fmi.avi.model.AviationWeatherMessage.ReportStatus; -import fi.fmi.avi.model.CloudForecast; -import fi.fmi.avi.model.NumericMeasure; -import fi.fmi.avi.model.PartialOrCompleteTimeInstant; -import fi.fmi.avi.model.PartialOrCompleteTimePeriod; -import fi.fmi.avi.model.SurfaceWind; -import fi.fmi.avi.model.Weather; -import fi.fmi.avi.model.taf.TAF; -import fi.fmi.avi.model.taf.TAFAirTemperatureForecast; -import fi.fmi.avi.model.taf.TAFBaseForecast; -import fi.fmi.avi.model.taf.TAFChangeForecast; -import fi.fmi.avi.model.taf.TAFForecast; -import icao.iwxxm21.AerodromeAirTemperatureForecastPropertyType; -import icao.iwxxm21.AerodromeAirTemperatureForecastType; -import icao.iwxxm21.AerodromeCloudForecastPropertyType; -import icao.iwxxm21.AerodromeCloudForecastType; -import icao.iwxxm21.AerodromeForecastChangeIndicatorType; -import icao.iwxxm21.AerodromeForecastWeatherType; -import icao.iwxxm21.AerodromeSurfaceWindForecastPropertyType; -import icao.iwxxm21.AerodromeSurfaceWindForecastType; -import icao.iwxxm21.AirportHeliportPropertyType; -import icao.iwxxm21.MeteorologicalAerodromeForecastRecordPropertyType; -import icao.iwxxm21.MeteorologicalAerodromeForecastRecordType; -import icao.iwxxm21.PermissibleUsageReasonType; -import icao.iwxxm21.PermissibleUsageType; -import icao.iwxxm21.RelationalOperatorType; -import icao.iwxxm21.TAFReportStatusType; -import icao.iwxxm21.TAFType; +import fi.fmi.avi.model.taf.*; +import icao.iwxxm21.*; +import net.opengis.gml32.*; +import net.opengis.om20.OMObservationPropertyType; +import net.opengis.om20.OMObservationType; +import net.opengis.om20.OMProcessPropertyType; +import net.opengis.om20.TimeObjectPropertyType; +import org.w3c.dom.Document; import wmo.metce2013.ProcessType; +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.InputStream; +import java.time.ZonedDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static fi.fmi.avi.model.AviationCodeListUser.CODELIST_VALUE_NIL_REASON_NOTHING_OF_OPERATIONAL_SIGNIFICANCE; + /** * Common functionality for conversions related to producing IWXXM TAF messages. * - * @param - * the type of the + * @param the type of the */ public abstract class TAFIWXXMSerializer extends AbstractIWXXM21Serializer { @@ -94,11 +49,8 @@ private static AviationCodeListUser.TAFStatus getTafStatus(final TAF input) { /** * Converts a TAF object into another format. * - * @param input - * input message - * @param hints - * parsing hints - * + * @param input input message + * @param hints parsing hints * @return the conversion result. */ @Override @@ -138,8 +90,8 @@ public ConversionResult convertMessage(final TAF input, final ConversionHints if (!input.isMissingMessage()) { if (input.getValidityTime().isPresent()) { - final String validityStart = input.getValidityTime(). flatMap(AbstractIWXXMSerializer::startToIWXXMDateTime).orElse(null); - final String validityEnd = input.getValidityTime(). flatMap(AbstractIWXXMSerializer::endToIWXXMDateTime).orElse(null); + final String validityStart = input.getValidityTime().flatMap(AbstractIWXXMSerializer::startToIWXXMDateTime).orElse(null); + final String validityEnd = input.getValidityTime().flatMap(AbstractIWXXMSerializer::endToIWXXMDateTime).orElse(null); if (validityStart == null || validityEnd == null) { result.addIssue(new ConversionIssue(Type.MISSING_DATA, "Validity time for TAF is missing complete start or end")); return result; @@ -182,7 +134,7 @@ public ConversionResult convertMessage(final TAF input, final ConversionHints } protected void updateBaseForecast(final TAF source, final TAFType target, final String issueTimeId, final String validTimeId, final String foiId, - final String processId, final String aerodromeId, final ConversionResult result) { + final String processId, final String aerodromeId, final ConversionResult result) { final Optional baseForecastInput = source.getBaseForecast(); if (baseForecastInput.isPresent()) { @@ -241,7 +193,7 @@ protected void updateBaseForecast(final TAF source, final TAFType target, final @SuppressWarnings("unchecked") protected void updateChangeForecast(final TAF source, final TAFType target, final String issueTimeId, final String validTimeId, final String foid, - final String processId, final ConversionResult result) { + final String processId, final ConversionResult result) { final Optional> fcts = source.getChangeForecasts(); final ZonedDateTime tafValidityStart = source.getValidityTime()// @@ -427,7 +379,7 @@ private void updateForecastSurfaceWind(final SurfaceWind source, final Aerodrome } private void setAirTemperatureForecast(final TAFAirTemperatureForecast source, final AerodromeAirTemperatureForecastType target, - final ConversionResult result) { + final ConversionResult result) { if (source != null) { NumericMeasure measure = source.getMinTemperature(); if (!source.getMinTemperatureTime().getCompleteTime().isPresent()) { @@ -566,18 +518,4 @@ protected IssueList validate(final String output, final XMLSchemaInfo schemaInfo } } - public static class ToJAXBObject extends TAFIWXXMSerializer { - - @Override - protected TAFType render(final TAFType taf, final ConversionHints hints) { - return taf; - } - - @Override - protected IssueList validate(final TAFType output, final XMLSchemaInfo schemaInfo, final ConversionHints hints) { - return TAFIWXXMSerializer.validateJAXBObjectAgainstSchemaAndSchematron(output, TAFType.class, schemaInfo, hints); - } - - } - } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Parser.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Parser.java index 4b714c7f..b210819d 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Parser.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Parser.java @@ -1,16 +1,18 @@ package fi.fmi.avi.converter.iwxxm.v3_0; -import fi.fmi.avi.converter.iwxxm.AbstractIWXXMParser; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMAixm511WxParser; +import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolverAixm511Wx; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; import fi.fmi.avi.model.AviationWeatherMessageOrCollection; -public abstract class AbstractIWXXM30Parser extends AbstractIWXXMParser { +public abstract class AbstractIWXXM30Parser extends AbstractIWXXMAixm511WxParser { @Override protected XMLSchemaInfo getSchemaInfo() { - final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(F_SECURE_PROCESSING); + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Wx.getInstance(), F_SECURE_PROCESSING); schemaInfo.addSchemaSource(icao.iwxxm30.ReportType.class.getResource("/int/icao/iwxxm/3.0.0/iwxxm.xsd")); schemaInfo.addSchematronRule(icao.iwxxm30.ReportType.class.getResource("/schematron/xslt/int/icao/iwxxm/3.0.0/rule/iwxxm.xsl")); return schemaInfo; } + } diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Serializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Serializer.java index 6e739afb..bd0018b7 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Serializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/AbstractIWXXM30Serializer.java @@ -1,14 +1,15 @@ package fi.fmi.avi.converter.iwxxm.v3_0; -import java.util.List; - -import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMAixm511WxSerializer; import fi.fmi.avi.converter.iwxxm.IWXXMNamespaceContext; +import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolverAixm511Wx; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; import fi.fmi.avi.model.AviationWeatherMessageOrCollection; import icao.iwxxm30.SpaceWeatherAdvisoryType; -public abstract class AbstractIWXXM30Serializer extends AbstractIWXXMSerializer { +import java.util.List; + +public abstract class AbstractIWXXM30Serializer extends AbstractIWXXMAixm511WxSerializer { private static IWXXMNamespaceContext nsCtx; private static synchronized IWXXMNamespaceContext getNSContext() { @@ -22,7 +23,7 @@ private static synchronized IWXXMNamespaceContext getNSContext() { @Override public XMLSchemaInfo getSchemaInfo() { - final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(F_SECURE_PROCESSING); + final XMLSchemaInfo schemaInfo = new XMLSchemaInfo(IWXXMSchemaResourceResolverAixm511Wx.getInstance(), F_SECURE_PROCESSING); schemaInfo.addSchemaSource(SpaceWeatherAdvisoryType.class.getResource("/int/icao/iwxxm/3.0.0/iwxxm.xsd")); schemaInfo.addSchematronRule(SpaceWeatherAdvisoryType.class.getResource("/schematron/xslt/int/icao/iwxxm/3.0.0/rule/iwxxm.xsl")); schemaInfo.addSchemaLocation("http://icao.int/iwxxm/3.0", "http://schemas.wmo.int/iwxxm/3.0/iwxxm.xsd"); diff --git a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/swx/SpaceWeatherIWXXMSerializer.java b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/swx/SpaceWeatherIWXXMSerializer.java index fa66b237..e7109456 100644 --- a/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/swx/SpaceWeatherIWXXMSerializer.java +++ b/src/main/java/fi/fmi/avi/converter/iwxxm/v3_0/swx/SpaceWeatherIWXXMSerializer.java @@ -1,63 +1,27 @@ package fi.fmi.avi.converter.iwxxm.v3_0.swx; -import java.io.InputStream; -import java.util.List; -import java.util.UUID; - -import javax.xml.bind.JAXBElement; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; - -import net.opengis.gml32.AbstractTimeObjectType; -import net.opengis.gml32.TimeIndeterminateValueType; -import net.opengis.gml32.TimeInstantPropertyType; -import net.opengis.gml32.TimeInstantType; -import net.opengis.gml32.TimePositionType; -import net.opengis.gml32.TimePrimitivePropertyType; - -import org.w3c.dom.Document; - -import aero.aixm511.AirspaceVolumeType; -import aero.aixm511.CodeOrganisationDesignatorType; -import aero.aixm511.CodeUnitType; -import aero.aixm511.CodeVerticalReferenceType; import aero.aixm511.SurfacePropertyType; -import aero.aixm511.TextNameType; -import aero.aixm511.UnitTimeSlicePropertyType; -import aero.aixm511.UnitTimeSliceType; -import aero.aixm511.UnitType; -import fi.fmi.avi.converter.ConversionException; -import fi.fmi.avi.converter.ConversionHints; -import fi.fmi.avi.converter.ConversionIssue; -import fi.fmi.avi.converter.ConversionResult; -import fi.fmi.avi.converter.IssueList; +import aero.aixm511.*; +import fi.fmi.avi.converter.*; +import fi.fmi.avi.converter.iwxxm.AbstractIWXXMAixm511WxSerializer; import fi.fmi.avi.converter.iwxxm.AbstractIWXXMSerializer; import fi.fmi.avi.converter.iwxxm.XMLSchemaInfo; import fi.fmi.avi.converter.iwxxm.v3_0.AbstractIWXXM30Serializer; import fi.fmi.avi.model.AviationCodeListUser; import fi.fmi.avi.model.PartialOrCompleteTimeInstant; -import fi.fmi.avi.model.swx.AirspaceVolume; -import fi.fmi.avi.model.swx.IssuingCenter; -import fi.fmi.avi.model.swx.NextAdvisory; -import fi.fmi.avi.model.swx.SpaceWeatherAdvisory; -import fi.fmi.avi.model.swx.SpaceWeatherAdvisoryAnalysis; -import fi.fmi.avi.model.swx.SpaceWeatherPhenomenon; -import fi.fmi.avi.model.swx.SpaceWeatherRegion; -import icao.iwxxm30.AbstractTimeObjectPropertyType; +import fi.fmi.avi.model.swx.*; import icao.iwxxm30.AirspaceVolumePropertyType; -import icao.iwxxm30.PermissibleUsageReasonType; -import icao.iwxxm30.PermissibleUsageType; -import icao.iwxxm30.ReportStatusType; -import icao.iwxxm30.SpaceWeatherAdvisoryType; -import icao.iwxxm30.SpaceWeatherAnalysisPropertyType; -import icao.iwxxm30.SpaceWeatherAnalysisType; -import icao.iwxxm30.SpaceWeatherLocationType; -import icao.iwxxm30.SpaceWeatherPhenomenaType; -import icao.iwxxm30.SpaceWeatherRegionPropertyType; -import icao.iwxxm30.SpaceWeatherRegionType; -import icao.iwxxm30.StringWithNilReasonType; -import icao.iwxxm30.TimeIndicatorType; import icao.iwxxm30.UnitPropertyType; +import icao.iwxxm30.*; +import net.opengis.gml32.*; +import org.w3c.dom.Document; + +import javax.xml.bind.JAXBElement; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import java.io.InputStream; +import java.util.List; +import java.util.UUID; public abstract class SpaceWeatherIWXXMSerializer extends AbstractIWXXM30Serializer { private static final int REQUIRED_NUMBER_OF_ANALYSES = 5; @@ -72,7 +36,7 @@ public ConversionResult convertMessage(final SpaceWeatherAdvisory input, fina final ConversionResult result = new ConversionResult<>(); final SpaceWeatherAdvisoryType swxType = create(SpaceWeatherAdvisoryType.class); - swxType.setId(UUID_PREFIX + UUID.randomUUID().toString()); + swxType.setId(UUID_PREFIX + UUID.randomUUID()); if (input.getIssueTime().isPresent()) { swxType.setIssueTime(create(TimeInstantPropertyType.class, prop -> getIssueTime(prop, input.getIssueTime().get()))); @@ -207,7 +171,7 @@ private SpaceWeatherAnalysisPropertyType toSpaceWeatherAnalysisPropertyType(fina analysisType.setTimeIndicator(TimeIndicatorType.FORECAST); } - analysisType.setId(UUID_PREFIX + UUID.randomUUID().toString()); + analysisType.setId(UUID_PREFIX + UUID.randomUUID()); analysisType.setPhenomenonTime(create(AbstractTimeObjectPropertyType.class, prop -> getAnalysisTime(prop, analysis.getTime()))); final int regionsAmount = analysis.getRegions().size(); @@ -258,7 +222,7 @@ private SpaceWeatherAnalysisPropertyType toSpaceWeatherAnalysisPropertyType(fina private void getAnalysisTime(final AbstractTimeObjectPropertyType prop, final PartialOrCompleteTimeInstant time) { final TimeInstantType timeInstantType = create(TimeInstantType.class); - timeInstantType.setId(UUID_PREFIX + UUID.randomUUID().toString()); + timeInstantType.setId(UUID_PREFIX + UUID.randomUUID()); final TimePositionType timePositionType = create(TimePositionType.class); toIWXXMDateTime(time).ifPresent(t -> timePositionType.getValue().add(t)); timeInstantType.setTimePosition(timePositionType); @@ -269,10 +233,10 @@ private void getAnalysisTime(final AbstractTimeObjectPropertyType prop, final Pa private void getIssuingCenter(final UnitPropertyType prop, final IssuingCenter issuingCenter) { final UnitType unitType = create(UnitType.class); - unitType.setId(UUID_PREFIX + UUID.randomUUID().toString()); + unitType.setId(UUID_PREFIX + UUID.randomUUID()); final UnitTimeSlicePropertyType unitTimeSlicePropertyType = create(UnitTimeSlicePropertyType.class); final UnitTimeSliceType unitTimeSliceType = create(UnitTimeSliceType.class); - unitTimeSliceType.setId(UUID_PREFIX + UUID.randomUUID().toString()); + unitTimeSliceType.setId(UUID_PREFIX + UUID.randomUUID()); final TextNameType textNameType = create(TextNameType.class); if (issuingCenter.getName().isPresent()) { textNameType.setValue(issuingCenter.getName().get()); @@ -300,27 +264,27 @@ private void getIssueTime(final TimeInstantPropertyType prop, final PartialOrCom final TimePositionType tp = create(TimePositionType.class); toIWXXMDateTime(time).ifPresent(t -> tp.getValue().add(t)); ti.setTimePosition(tp); - ti.setId(UUID_PREFIX + UUID.randomUUID().toString()); + ti.setId(UUID_PREFIX + UUID.randomUUID()); prop.setTimeInstant(ti); } private void getAirspaceVolumeProperty(final AirspaceVolumePropertyType prop, final AirspaceVolume volume) { final AirspaceVolumeType airspaceVolumeType = create(AirspaceVolumeType.class); if (volume.getHorizontalProjection().isPresent()) { - airspaceVolumeType.setId(UUID_PREFIX + UUID.randomUUID().toString()); - final SurfacePropertyType surfaceProperty = createSurface(volume.getHorizontalProjection().get(), UUID_PREFIX + UUID.randomUUID().toString()); + airspaceVolumeType.setId(UUID_PREFIX + UUID.randomUUID()); + final SurfacePropertyType surfaceProperty = createSurface(volume.getHorizontalProjection().get(), UUID_PREFIX + UUID.randomUUID()); airspaceVolumeType.setHorizontalProjection(surfaceProperty); } volume.getLowerLimit().ifPresent(limit -> { // TODO: Once xsi:nil can be set, setLowerLimit can be moved outside presence test - airspaceVolumeType.setLowerLimit(toValDistanceVertical(limit).orElseGet(AbstractIWXXMSerializer::nilValDistanceVertical)); + airspaceVolumeType.setLowerLimit(toValDistanceVertical(limit).orElseGet(AbstractIWXXMAixm511WxSerializer::nilValDistanceVertical)); airspaceVolumeType.setLowerLimitReference(create(CodeVerticalReferenceType.class, codeVerticalReferenceType -> volume.getLowerLimitReference().ifPresent(codeVerticalReferenceType::setValue))); }); volume.getUpperLimit().ifPresent(limit -> { // TODO: Once xsi:nil can be set, setLowerLimit can be moved outside presence test - airspaceVolumeType.setUpperLimit(toValDistanceVertical(limit).orElseGet(AbstractIWXXMSerializer::nilValDistanceVertical)); + airspaceVolumeType.setUpperLimit(toValDistanceVertical(limit).orElseGet(AbstractIWXXMAixm511WxSerializer::nilValDistanceVertical)); airspaceVolumeType.setUpperLimitReference(create(CodeVerticalReferenceType.class, codeVerticalReferenceType -> volume.getUpperLimitReference().ifPresent(codeVerticalReferenceType::setValue))); }); @@ -339,7 +303,7 @@ private void getNextAdvisory(final TimeInstantPropertyType prop, final NextAdvis nextAdvisory.getTime()// . flatMap(AbstractIWXXMSerializer::toIWXXMDateTime)// .ifPresent(t -> timePosition.getValue().add(t)); - timeInstant.setId(UUID_PREFIX + UUID.randomUUID().toString()); + timeInstant.setId(UUID_PREFIX + UUID.randomUUID()); timeInstant.setTimePosition(timePosition); prop.setTimeInstant(timeInstant); } else { diff --git a/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETCleanup.xsl b/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETCleanup.xsl new file mode 100644 index 00000000..078e91df --- /dev/null +++ b/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet/AIRMETCleanup.xsl @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETCleanup.xsl b/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETCleanup.xsl new file mode 100644 index 00000000..d9d6938f --- /dev/null +++ b/src/main/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet/SIGMETCleanup.xsl @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/fi/fmi/avi/converter/iwxxm/IWXXMTestConfiguration.java b/src/test/java/fi/fmi/avi/converter/iwxxm/IWXXMTestConfiguration.java index d1e22905..4f273334 100644 --- a/src/test/java/fi/fmi/avi/converter/iwxxm/IWXXMTestConfiguration.java +++ b/src/test/java/fi/fmi/avi/converter/iwxxm/IWXXMTestConfiguration.java @@ -1,17 +1,9 @@ package fi.fmi.avi.converter.iwxxm; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.w3c.dom.Document; - import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; - import fi.fmi.avi.converter.AviMessageConverter; import fi.fmi.avi.converter.AviMessageSpecificConverter; import fi.fmi.avi.converter.iwxxm.conf.IWXXMConverter; @@ -25,6 +17,12 @@ import fi.fmi.avi.model.swx.SpaceWeatherBulletin; import fi.fmi.avi.model.taf.TAF; import fi.fmi.avi.model.taf.TAFBulletin; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.w3c.dom.Document; @Configuration @Import(IWXXMConverter.class) @@ -112,6 +110,14 @@ public class IWXXMTestConfiguration { @Qualifier("sigmetIWXXM30DOMSerializer") private AviMessageSpecificConverter sigmetIWXXM30DOMSerializer; + @Autowired + @Qualifier("sigmetIWXXM20231StringSerializer") + private AviMessageSpecificConverter sigmetIWXXM20231StringSerializer; + + @Autowired + @Qualifier("sigmetIWXXM20231DOMSerializer") + private AviMessageSpecificConverter sigmetIWXXM20231DOMSerializer; + @Autowired @Qualifier("airmetIWXXMStringSerializer") private AviMessageSpecificConverter airmetIWXXMStringSerializer; @@ -127,6 +133,15 @@ public class IWXXMTestConfiguration { @Autowired @Qualifier("airmetIWXXM30DOMSerializer") private AviMessageSpecificConverter airmetIWXXM30DOMSerializer; + + @Autowired + @Qualifier("airmetIWXXM20231StringSerializer") + private AviMessageSpecificConverter airmetIWXXM20231StringSerializer; + + @Autowired + @Qualifier("airmetIWXXM20231DOMSerializer") + private AviMessageSpecificConverter airmetIWXXM20231DOMSerializer; + // Space weather @Autowired @@ -213,10 +228,14 @@ public AviMessageConverter aviMessageConverter() { p.setMessageSpecificConverter(IWXXMConverter.SIGMET_POJO_TO_IWXXM21_STRING, sigmetIWXXMStringSerializer); p.setMessageSpecificConverter(IWXXMConverter.SIGMET_POJO_TO_IWXXM30_DOM, sigmetIWXXM30DOMSerializer); p.setMessageSpecificConverter(IWXXMConverter.SIGMET_POJO_TO_IWXXM30_STRING, sigmetIWXXM30StringSerializer); + p.setMessageSpecificConverter(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_DOM, sigmetIWXXM20231DOMSerializer); + p.setMessageSpecificConverter(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING, sigmetIWXXM20231StringSerializer); p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM21_DOM, airmetIWXXMDOMSerializer); p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM21_STRING, airmetIWXXMStringSerializer); p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM30_DOM, airmetIWXXM30DOMSerializer); p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM30_STRING, airmetIWXXM30StringSerializer); + p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_DOM, airmetIWXXM20231DOMSerializer); + p.setMessageSpecificConverter(IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_STRING, airmetIWXXM20231StringSerializer); // Space weather p.setMessageSpecificConverter(IWXXMConverter.IWXXM30_STRING_TO_SPACE_WEATHER_POJO, spaceWeatherIWXXMStringParser); diff --git a/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/AIRMETIWWXXMSerializerTest.java b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/AIRMETIWWXXMSerializerTest.java new file mode 100644 index 00000000..5034ed93 --- /dev/null +++ b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/AIRMETIWWXXMSerializerTest.java @@ -0,0 +1,181 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import fi.fmi.avi.converter.AviMessageConverter; +import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.ConversionResult; +import fi.fmi.avi.converter.iwxxm.IWXXMTestConfiguration; +import fi.fmi.avi.converter.iwxxm.conf.IWXXMConverter; +import fi.fmi.avi.model.sigmet.AIRMET; +import fi.fmi.avi.model.sigmet.immutable.AIRMETImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.w3c.dom.Document; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static fi.fmi.avi.converter.iwxxm.IWXXMConverterTests.assertXMLEqualsIgnoringVariables; +import static junit.framework.TestCase.assertSame; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = IWXXMTestConfiguration.class, loader = AnnotationConfigContextLoader.class) +public class AIRMETIWWXXMSerializerTest { + + @Autowired + ObjectMapper om; + @Autowired + private AviMessageConverter converter; + + protected AIRMET readFromJSON(final String fileName) throws IOException { + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new Jdk8Module()); + objectMapper.registerModule(new JavaTimeModule()); + try (InputStream inputStream = this.getClass().getResourceAsStream(fileName)) { + if (inputStream != null) { + return objectMapper.readValue(inputStream, AIRMETImpl.class); + } else { + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + } + + protected String readFromFile(final String fileName) throws IOException { + try { + return new String(Files.readAllBytes(Paths.get(getClass().getResource(fileName).toURI()))); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + + public void doTestAIRMETDOMSerialization(final String fn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_DOM)); + final AIRMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_DOM); + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss); + } + assertTrue(result.getConvertedMessage().isPresent()); + } + + @Test + public void dotestAIRMETStringSerialization1() throws Exception { + doTestAIRMETStringSerialization("airmet_iwxxm1.json", "airmet_iwxxm1.xml"); + } + + @Test + public void dotestAIRMETMOVING() throws Exception { + String s = doTestAIRMETStringSerialization("airmetMOVING.json", "airmetMOVING.xml"); + } + + @Test + public void dotestAIRMETCleanup() throws Exception { + doTestAIRMETStringSerialization("airmetMOVING.json", "airmetMOVING.xml"); + } + + @Test + public void dotestAIRMETSTNR() throws Exception { + doTestAIRMETStringSerialization("airmetSTNR.json", "airmetSTNR.xml"); + } + + @Test + public void dotestAIRMETStringSerialization3() throws Exception { + doTestAIRMETStringSerialization("airmet2.json", "airmet2.xml"); + } + + @Test + public void dotestAIRMETStringSerialization4() throws Exception { + doTestAIRMETStringSerialization("airmet_bkncld.json", "airmet_bkncld.xml"); + } + + @Test + public void dotestAIRMETStringSerialization5() throws Exception { + doTestAIRMETStringSerialization("airmet_ovccld.json", "airmet_ovccld.xml"); + } + + @Test + public void dotestAIRMETStringSerialization6() throws Exception { + //Because IWXXM 2023-1 can not represent ovccld with ABV the result should be the same as without ABV + doTestAIRMETStringSerialization("airmet_ovccld_abv.json", "airmet_ovccld.xml"); + } + + @Test + public void dotestAIRMETStringSerialization_wind() throws Exception { + doTestAIRMETStringSerialization("airmet_wind.json", "airmet_wind.xml"); + } + + @Test + public void dotestAIRMETStringSerialization_vis() throws Exception { + doTestAIRMETStringSerialization("airmet_vis.json", "airmet_vis.xml"); + } + + @Test + public void dotestAIRMET_OBS_BEFORE_StringSerialization3() throws Exception { + doTestAIRMETStringSerialization("airmet2_obs_before.json", "airmet2_obs_before.xml"); + } + + @Test + public void dotestAIRMET_OPER() throws Exception { + String s = doTestAIRMETStringSerialization("airmet_OPER.json", "airmet_OPER.xml"); + } + + @Test + public void dotestAIRMET_TEST() throws Exception { + String s = doTestAIRMETStringSerialization("airmet_TEST.json", "airmet_TEST.xml"); + } + + @Test + public void dotestAIRMET_EXER() throws Exception { + String s = doTestAIRMETStringSerialization("airmet_EXER.json", "airmet_EXER.xml"); + } + + + @Test + public void dotestAIRMETDOMSerialization1() throws Exception { + doTestAIRMETDOMSerialization("airmet_iwxxm1.json"); + } + + @Test + public void dotestAIRMETDOMSerialization2() throws Exception { + doTestAIRMETDOMSerialization("airmetMOVING.json"); + } + + @Test + public void dotestAIRMETDOMSerialization3() throws Exception { + doTestAIRMETDOMSerialization("airmet2.json"); + } + + public String doTestAIRMETStringSerialization(final String fn, final String iwxxmFn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_STRING)); + final AIRMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.AIRMET_POJO_TO_IWXXM2023_1_STRING); + + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss.getMessage() + "===" + iss.getCause()); + } + + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + System.err.println("XML:\n" + result.getConvertedMessage().get()); + assertTrue(result.getConvertedMessage().isPresent()); + assertNotNull(result.getConvertedMessage().get()); + + assertXMLEqualsIgnoringVariables(readFromFile(iwxxmFn), result.getConvertedMessage().get()); + return result.getConvertedMessage().orElse(null); + } + +} diff --git a/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/SIGMETIWWXXMSerializerTest.java b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/SIGMETIWWXXMSerializerTest.java new file mode 100644 index 00000000..7f05977e --- /dev/null +++ b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/SIGMETIWWXXMSerializerTest.java @@ -0,0 +1,197 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import fi.fmi.avi.converter.AviMessageConverter; +import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.ConversionResult; +import fi.fmi.avi.converter.iwxxm.IWXXMTestConfiguration; +import fi.fmi.avi.converter.iwxxm.conf.IWXXMConverter; +import fi.fmi.avi.model.sigmet.SIGMET; +import fi.fmi.avi.model.sigmet.immutable.SIGMETImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static fi.fmi.avi.converter.iwxxm.IWXXMConverterTests.assertXMLEqualsIgnoringVariables; +import static junit.framework.TestCase.*; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = IWXXMTestConfiguration.class, loader = AnnotationConfigContextLoader.class) + +public class SIGMETIWWXXMSerializerTest { + @Autowired + private AviMessageConverter converter; + + protected SIGMET readFromJSON(final String fileName) throws IOException { + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new Jdk8Module()); + objectMapper.registerModule(new JavaTimeModule()); + try (InputStream inputStream = this.getClass().getResourceAsStream(fileName)) { + if (inputStream != null) { + return objectMapper.readValue(inputStream, SIGMETImpl.class); + } else { + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + } + + protected String readFromFile(final String fileName) throws IOException { + try { + return new String(Files.readAllBytes(Paths.get(getClass().getResource(fileName).toURI()))); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + + @Test + public void testSIGMETStringSerialization1() throws Exception { + doTestSIGMETStringSerialization("sigmet1.json", "sigmet1.xml"); + } + + @Test + public void testSIGMETStringSerialization2() throws Exception { + doTestSIGMETStringSerialization("sigmet2.json", "sigmet2.xml"); + } + + @Test + public void testSIGMETStringSerialization3() throws Exception { + doTestSIGMETStringSerialization("sigmet3.json", "sigmet3.xml"); + } + + @Test + public void testSIGMETStringSerializationCancel() throws Exception { + doTestSIGMETStringSerialization("sigmet_cancel.json", "sigmet_cancel.xml"); + } + + @Test + public void testSIGMETSTNR() throws Exception { + // SIGMET describes stationary phenomenon, should result in an IWXXM containing + // an empty iwxxm:directionOfMotion with nilReason "http://codes.wmo.int/common/nil/inapplicable" + // and iwxxm:speedOfMotion of 0 + doTestSIGMETStringSerialization("sigmetSTNR.json", "sigmetSTNR.xml"); + } + + @Test + public void testSIGMETMOVING() throws Exception { + //SIGMET has a speed and direction, should result in IWXXM containing + // directionOfMotion and speedOfMotion elements + doTestSIGMETStringSerialization("sigmetMOVING.json", "sigmetMOVING.xml"); + } + + @Test + public void testSIGMETCleanup() throws Exception { + //Asserts the generated SIGMET is cleaned up correctly + doTestSIGMETStringSerialization("sigmetMOVING.json", "sigmetMOVING.xml"); + } + + @Test + public void testSIGMETForecastPosition() throws Exception { + //SIGMET with forecast position for phenomenon + //should result in IWXXM with no speedOfMotion or directionOfMotion elements + doTestSIGMETStringSerialization("sigmetFORECASTPOSITION.json", "sigmetFORECASTPOSITION.xml"); + } + + @Test + public void testSIGMETForecastPositionNoFcTime() throws Exception { + //SIGMET with forecast position for phenomenon + //should result in IWXXM with no speedOfMotion or directionOfMotion elements + doTestSIGMETStringSerialization("sigmetFORECASTPOSITION_NOFCTIME.json", "sigmetFORECASTPOSITION_NOFCTIME.xml"); + } + + @Test + public void testFL() throws Exception { + doTestSIGMETStringSerialization("sigmet_FL.json", "sigmet_FL.xml"); + } + + @Test + public void testM() throws Exception { + doTestSIGMETStringSerialization("sigmet_M.json", "sigmet_M.xml"); + } + + @Test + public void testFT() throws Exception { + doTestSIGMETStringSerialization("sigmet_FT.json", "sigmet_FT.xml"); + } + + @Test + public void testSFCFL() throws Exception { + doTestSIGMETStringSerialization("sigmet_SFC_FL.json", "sigmet_SFC_FL.xml"); + } + + @Test + public void testTOPABV() throws Exception { + doTestSIGMETStringSerialization("sigmet_TOPABV_FL.json", "sigmet_TOPABV_FL.xml"); + } + + @Test + public void testTOPBLW() throws Exception { + doTestSIGMETStringSerialization("sigmet_TOPBLW_FL.json", "sigmet_TOPBLW_FL.xml"); + } + + @Test + public void testABV() throws Exception { + doTestSIGMETStringSerialization("sigmet_ABV_FL.json", "sigmet_ABV_FL.xml"); + } + + @Test + public void test_OBS_BEFORE() throws Exception { + doTestSIGMETStringSerialization("sigmet_OBSBEFORE.json", "sigmet_OBSBEFORE.xml"); + } + + @Test + public void test_VA_OBS_BEFORE() throws Exception { + doTestSIGMETStringSerialization("vasigmet1_OBSBEFORE.json", "vasigmet1_OBSBEFORE.xml"); + } + + @Test + public void testABV_NOOBSTIME() throws Exception { + doTestSIGMETStringSerialization("sigmet_ABV_FL_NOOBSTIME.json", "sigmet_ABV_FL_NOOBSTIME.xml"); + } + + @Test + public void testTest() throws Exception { + doTestSIGMETStringSerialization("sigmet_FL_TEST.json", "sigmet_FL_TEST.xml"); + } + + @Test + public void testExercise() throws Exception { + doTestSIGMETStringSerialization("sigmet_FL_EXER.json", "sigmet_FL_EXER.xml"); + } + + @Test + public void testOperational() throws Exception { + doTestSIGMETStringSerialization("sigmet_FL_OPER.json", "sigmet_FL_OPER.xml"); + } + + public String doTestSIGMETStringSerialization(final String fn, final String iwxxmFn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING)); + final SIGMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING); + + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss.getMessage() + "===" + iss.getCause()); + } + + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + System.err.println("XML:\n" + result.getConvertedMessage().get()); + assertTrue(result.getConvertedMessage().isPresent()); + assertNotNull(result.getConvertedMessage().get()); + + assertXMLEqualsIgnoringVariables(readFromFile(iwxxmFn), result.getConvertedMessage().get()); + return result.getConvertedMessage().orElse(null); + } +} diff --git a/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/VASIGMETIWWXXMSerializerTest.java b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/VASIGMETIWWXXMSerializerTest.java new file mode 100644 index 00000000..786d778b --- /dev/null +++ b/src/test/java/fi/fmi/avi/converter/iwxxm/v2023_1/VASIGMETIWWXXMSerializerTest.java @@ -0,0 +1,140 @@ +package fi.fmi.avi.converter.iwxxm.v2023_1; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import fi.fmi.avi.converter.AviMessageConverter; +import fi.fmi.avi.converter.ConversionIssue; +import fi.fmi.avi.converter.ConversionResult; +import fi.fmi.avi.converter.iwxxm.IWXXMTestConfiguration; +import fi.fmi.avi.converter.iwxxm.conf.IWXXMConverter; +import fi.fmi.avi.model.sigmet.SIGMET; +import fi.fmi.avi.model.sigmet.immutable.SIGMETImpl; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static fi.fmi.avi.converter.iwxxm.IWXXMConverterTests.assertXMLEqualsIgnoringVariables; +import static junit.framework.TestCase.assertSame; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = IWXXMTestConfiguration.class, loader = AnnotationConfigContextLoader.class) + +public class VASIGMETIWWXXMSerializerTest { + @Autowired + private AviMessageConverter converter; + + protected SIGMET readFromJSON(final String fileName) throws IOException { + final ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.registerModule(new Jdk8Module()); + objectMapper.registerModule(new JavaTimeModule()); + try (InputStream inputStream = this.getClass().getResourceAsStream(fileName)) { + if (inputStream != null) { + return objectMapper.readValue(inputStream, SIGMETImpl.class); + } else { + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + } + + protected String readFromFile(final String fileName) throws IOException { + try { + return new String(Files.readAllBytes(Paths.get(getClass().getResource(fileName).toURI()))); + } catch (URISyntaxException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + throw new FileNotFoundException("Resource '" + fileName + "' could not be loaded"); + } + } + + @Test + public void testVaSigmet() throws Exception { + doTestSIGMETStringSerialization("vasigmet1.json", "vasigmet1.xml"); + } + + @Test + public void testVaSigmet_NoCoords() throws Exception { + doTestSIGMETStringSerializationNoCoords("vasigmet1_nocoords.json", "vasigmet1_nocoords.xml"); + } + + @Test + public void testVaSigmet_NoName() throws Exception { + doTestSIGMETStringSerialization("vasigmet1_noname.json", "vasigmet1_noname.xml"); + } + + @Test + public void testVaSigmetCancel() throws Exception { + doTestSIGMETStringSerialization("vasigmet1_cancel.json", "vasigmet1_cancel.xml"); + } + + @Test + public void testVaSigmetCancelMovToFir() throws Exception { + doTestSIGMETStringSerialization("vasigmet1_cancel_movtofir.json", "vasigmet1_cancel_movtofir.xml"); + } + + public void testSIGMETStringSerialization(String fn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING)); + final SIGMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING); + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss); + } + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + assertTrue(result.getConvertedMessage().isPresent()); + } + + public String doTestSIGMETStringSerialization(final String fn, final String iwxxmFn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING)); + final SIGMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING); + + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss.getMessage() + "===" + iss.getCause()); + } + + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + System.err.println("XML:\n" + result.getConvertedMessage().get()); + assertTrue(result.getConvertedMessage().isPresent()); + assertNotNull(result.getConvertedMessage().get()); + + assertXMLEqualsIgnoringVariables(readFromFile(iwxxmFn), result.getConvertedMessage().get()); + return result.getConvertedMessage().orElse(null); + } + + public String doTestSIGMETStringSerializationNoCoords(final String fn, final String iwxxmFn) throws Exception { + assertTrue(converter.isSpecificationSupported(IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING)); + final SIGMET s = readFromJSON(fn); + final ConversionResult result = converter.convertMessage(s, IWXXMConverter.SIGMET_POJO_TO_IWXXM2023_1_STRING); + + boolean letItPass = false; + for (ConversionIssue iss : result.getConversionIssues()) { + System.err.println("iss:" + iss.getMessage() + "===" + iss.getCause()); + if (iss.toString().contains("SIGMET-6")) { + letItPass = true; + } + } + + if (!letItPass) { //Skip assertion if error is in rule VolcanicAshSIGMET-6 + assertSame(ConversionResult.Status.SUCCESS, result.getStatus()); + } + assertTrue(result.getConvertedMessage().isPresent()); + assertNotNull(result.getConvertedMessage().get()); + + System.out.println(result.getConvertedMessage().get()); + assertXMLEqualsIgnoringVariables(readFromFile(iwxxmFn), result.getConvertedMessage().get()); + return result.getConvertedMessage().orElse(null); + } + +} diff --git a/src/test/java/fi/fmi/avi/converter/iwxxm/v2_1/METARScannerTest.java b/src/test/java/fi/fmi/avi/converter/iwxxm/v2_1/METARScannerTest.java index 06a7f35e..505647d6 100644 --- a/src/test/java/fi/fmi/avi/converter/iwxxm/v2_1/METARScannerTest.java +++ b/src/test/java/fi/fmi/avi/converter/iwxxm/v2_1/METARScannerTest.java @@ -1,50 +1,38 @@ package fi.fmi.avi.converter.iwxxm.v2_1; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.time.ZonedDateTime; -import java.util.List; -import java.util.function.Consumer; - -import javax.xml.XMLConstants; -import javax.xml.bind.Binder; -import javax.xml.bind.JAXBContext; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; - -import org.junit.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; - import fi.fmi.avi.converter.ConversionHints; import fi.fmi.avi.converter.ConversionIssue; -import fi.fmi.avi.converter.iwxxm.DOMParsingTestBase; -import fi.fmi.avi.converter.iwxxm.GenericReportProperties; -import fi.fmi.avi.converter.iwxxm.IWXXMConverterBase; -import fi.fmi.avi.converter.iwxxm.IWXXMSchemaResourceResolver; -import fi.fmi.avi.converter.iwxxm.ReferredObjectRetrievalContext; +import fi.fmi.avi.converter.iwxxm.*; import fi.fmi.avi.converter.iwxxm.v2_1.metar.METARIWXXMScanner; import fi.fmi.avi.converter.iwxxm.v2_1.metar.METARProperties; import fi.fmi.avi.converter.iwxxm.v2_1.metar.ObservationRecordProperties; import fi.fmi.avi.converter.iwxxm.v2_1.metar.TrendForecastRecordProperties; -import fi.fmi.avi.model.Aerodrome; -import fi.fmi.avi.model.AviationCodeListUser; -import fi.fmi.avi.model.ElevatedPoint; -import fi.fmi.avi.model.PartialOrCompleteTime; -import fi.fmi.avi.model.PartialOrCompleteTimeInstant; +import fi.fmi.avi.model.*; import icao.iwxxm21.METARType; import icao.iwxxm21.ReportType; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Node; import wmo.metce2013.ProcessType; +import javax.xml.XMLConstants; +import javax.xml.bind.Binder; +import javax.xml.bind.JAXBContext; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.Assert.*; + public class METARScannerTest extends DOMParsingTestBase { private List withCollectedPropertiesFrom(final String fileName, final Consumer resultHandler) throws Exception { final Document doc = readDocument(METARScannerTest.class, fileName); - final JAXBContext ctx = IWXXMConverterBase.getJAXBContext(); + final JAXBContext ctx = AbstractIWXXMAixm511WxSerializer.getAixm511WxJAXBContext(); final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - final IWXXMSchemaResourceResolver resolver = IWXXMSchemaResourceResolver.getInstance(); + final IWXXMSchemaResourceResolver resolver = IWXXMSchemaResourceResolverAixm511Wx.getInstance(); schemaFactory.setResourceResolver(resolver); //Secure processing does not allow "file" protocol loading for schemas: schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.json new file mode 100644 index 00000000..3327da51 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.json @@ -0,0 +1,75 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "phenomenon": "MOD_ICE", + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "lowerLimit": { + "value": 10.0, + "uom": "FL" + }, + "upperLimit": { + "value": 35.0, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.xml new file mode 100644 index 00000000..2c726a60 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2.xml @@ -0,0 +1,108 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.json new file mode 100644 index 00000000..49d3d2be --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.json @@ -0,0 +1,75 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "phenomenon": "MOD_ICE", + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2017-08-27T11:15:00Z" + }, + "lowerLimit": { + "value": 10.0, + "uom": "FL" + }, + "upperLimit": { + "value": 35.0, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.xml new file mode 100644 index 00000000..1183f472 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet2_obs_before.xml @@ -0,0 +1,108 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.json new file mode 100644 index 00000000..8c908bcb --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.json @@ -0,0 +1,82 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "MOD_ICE", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 10.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false, + "lowerLimit": { + "value": 10.0, + "uom": "FL" + }, + "upperLimit": { + "value": 35.0, + "uom": "FL" + } + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.xml new file mode 100644 index 00000000..dda04d0d --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetMOVING.xml @@ -0,0 +1,106 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 180.0 + 10.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.json new file mode 100644 index 00000000..377cb553 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "MOD_ICE", + "analysisGeometries": [ + { + "movingSpeed": { + "value": 0.0, + "uom": "[kn_i]" + }, + "intensityChange": "NO_CHANGE", + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false, + "lowerLimit": { + "value": 10.0, + "uom": "FL" + }, + "upperLimit": { + "value": 35.0, + "uom": "FL" + } + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.xml new file mode 100644 index 00000000..e8724cfa --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmetSTNR.xml @@ -0,0 +1,108 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.json new file mode 100644 index 00000000..795ebade --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.json @@ -0,0 +1,75 @@ +{ + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T14:00:00Z" + } + }, + "phenomenon": "ISOL_TSGR", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.xml new file mode 100644 index 00000000..ead70676 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_EXER.xml @@ -0,0 +1,108 @@ + + + + + 2018-08-27T12:00:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T14:00:00Z + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.json new file mode 100644 index 00000000..1f6aa9fd --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.json @@ -0,0 +1,74 @@ +{ + "permissibleUsage": "OPERATIONAL", + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T14:00:00Z" + } + }, + "phenomenon": "ISOL_TSGR", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.xml new file mode 100644 index 00000000..13f9eed9 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_OPER.xml @@ -0,0 +1,107 @@ + + + + + 2018-08-27T12:00:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T14:00:00Z + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.json new file mode 100644 index 00000000..948178ae --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.json @@ -0,0 +1,75 @@ +{ + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "TEST", + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T14:00:00Z" + } + }, + "phenomenon": "ISOL_TSGR", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.xml new file mode 100644 index 00000000..a5cffbbe --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_TEST.xml @@ -0,0 +1,108 @@ + + + + + 2018-08-27T12:00:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T14:00:00Z + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.json new file mode 100644 index 00000000..088b56f9 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.json @@ -0,0 +1,84 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "BKN_CLD", + "cloudLevels": { + "cloudBase": { + "value": 100.0, + "uom": "FT" + }, + "cloudTop": { + "value": 5000, + "uom": "FT" + } + }, + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 10.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.xml new file mode 100644 index 00000000..c31350a7 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_bkncld.xml @@ -0,0 +1,106 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 180.0 + 10.0 + 100.0 + STD + 5000.0 + STD + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.json new file mode 100644 index 00000000..795ebade --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.json @@ -0,0 +1,75 @@ +{ + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T14:00:00Z" + } + }, + "phenomenon": "ISOL_TSGR", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.xml new file mode 100644 index 00000000..af9c8c8d --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_iwxxm1.xml @@ -0,0 +1,108 @@ + + + + + 2018-08-27T12:00:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T14:00:00Z + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + 0.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.json new file mode 100644 index 00000000..acd90dc5 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.json @@ -0,0 +1,84 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "OVC_CLD", + "cloudLevels": { + "cloudBase": { + "value": 100.0, + "uom": "FT" + }, + "cloudTop": { + "value": 5000, + "uom": "FT" + } + }, + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 10.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.xml new file mode 100644 index 00000000..0497b3fc --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld.xml @@ -0,0 +1,106 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 180.0 + 10.0 + 100.0 + STD + 5000.0 + STD + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld_abv.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld_abv.json new file mode 100644 index 00000000..282cc231 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_ovccld_abv.json @@ -0,0 +1,85 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "OVC_CLD", + "cloudLevels": { + "cloudBase": { + "value": 100.0, + "uom": "FT" + }, + "cloudTop": { + "value": 5000, + "uom": "FT" + }, + "topAbove": "true" + }, + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 10.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.json new file mode 100644 index 00000000..568913a3 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.json @@ -0,0 +1,82 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "SFC_VIS", + "visibility": { + "value": 500.0, + "uom": "M" + }, + "obscuration": [ + "FG", + "DZ" + ], + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 10.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.xml new file mode 100644 index 00000000..30e568a3 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_vis.xml @@ -0,0 +1,105 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 180.0 + 10.0 + 500.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.json new file mode 100644 index 00000000..2e177f6a --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.json @@ -0,0 +1,84 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "endTime": { + "completeTime": "2017-08-27T13:30:00Z" + } + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "phenomenon": "SFC_WIND", + "wind": { + "speed": { + "value": 10, + "uom": "[kn_i]" + }, + "direction": { + "value": 180, + "uom": "deg" + } + }, + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 180, + "uom": "deg" + }, + "movingSpeed": { + "value": 10, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + }, + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "approximateLocation": false + } + ] +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.xml new file mode 100644 index 00000000..c1894666 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/airmet_wind.xml @@ -0,0 +1,104 @@ + + + + + 2017-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2017-08-27T11:30:00Z + 2017-08-27T13:30:00Z + + + + + + + + 2017-08-27T11:30:00Z + + + + + + + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 180.0 + 10.0 + 180.0 + 10.0 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.json new file mode 100644 index 00000000..4853c819 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.json @@ -0,0 +1,81 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.xml new file mode 100644 index 00000000..9fef716f --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet1.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.json new file mode 100644 index 00000000..287df858 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.json @@ -0,0 +1,59 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.xml new file mode 100644 index 00000000..3da6b050 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet2.xml @@ -0,0 +1,119 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + + 0.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.json new file mode 100644 index 00000000..557ec10c --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.json @@ -0,0 +1,76 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 6.0, + 54.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.xml new file mode 100644 index 00000000..817b53dc --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet3.xml @@ -0,0 +1,155 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + + + + + 6.0 54.0 + 0.0 + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.json new file mode 100644 index 00000000..557ec10c --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.json @@ -0,0 +1,76 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 6.0, + 54.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.xml new file mode 100644 index 00000000..6b1d404b --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION.xml @@ -0,0 +1,155 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + + + + + 6.0 54.0 + 0.0 + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.json new file mode 100644 index 00000000..fafb2b19 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.json @@ -0,0 +1,73 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 6.0, + 54.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.xml new file mode 100644 index 00000000..59c5b3df --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetFORECASTPOSITION_NOFCTIME.xml @@ -0,0 +1,151 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 6.0 54.0 + 0.0 + + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.json new file mode 100644 index 00000000..23475a6e --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.json @@ -0,0 +1,67 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "movingSpeed": { + "value": 10, + "uom": "[kn_i]" + }, + "movingDirection": { + "value": 180, + "uom": "deg" + }, + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.xml new file mode 100644 index 00000000..42783a01 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetMOVING.xml @@ -0,0 +1,117 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + 180.0 + 10.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.json new file mode 100644 index 00000000..140557f3 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.json @@ -0,0 +1,63 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "movingSpeed": { + "value": 0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Point", + "coordinates": [ + 5.0, + 52.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.xml new file mode 100644 index 00000000..6631269c --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmetSTNR.xml @@ -0,0 +1,119 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + + + + + 5.0 52.0 + 0.0 + + + + + + + + + + + + + + 0.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.json new file mode 100644 index 00000000..995ec370 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 35, + "uom": "FL" + }, + "lowerLimitOperator": "ABOVE", + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.xml new file mode 100644 index 00000000..5479f1e4 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL.xml @@ -0,0 +1,109 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.json new file mode 100644 index 00000000..d182f8df --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.json @@ -0,0 +1,75 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "lowerLimit": { + "value": 35, + "uom": "FL" + }, + "lowerLimitOperator": "ABOVE", + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.xml new file mode 100644 index 00000000..8ba4b071 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_ABV_FL_NOOBSTIME.xml @@ -0,0 +1,105 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + + + + 35 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.json new file mode 100644 index 00000000..4853c819 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.json @@ -0,0 +1,81 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.xml new file mode 100644 index 00000000..ac55642b --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.json new file mode 100644 index 00000000..2c937467 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.json @@ -0,0 +1,83 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "EXERCISE", + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.xml new file mode 100644 index 00000000..5e20c1d1 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_EXER.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.json new file mode 100644 index 00000000..73fd26be --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.json @@ -0,0 +1,82 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "permissibleUsage": "OPERATIONAL", + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.xml new file mode 100644 index 00000000..949b9fac --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_OPER.xml @@ -0,0 +1,110 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.json new file mode 100644 index 00000000..0dbc6ca6 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.json @@ -0,0 +1,83 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "permissibleUsage": "NON_OPERATIONAL", + "permissibleUsageReason": "TEST", + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.xml new file mode 100644 index 00000000..66895cb2 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FL_TEST.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.json new file mode 100644 index 00000000..31979f65 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.json @@ -0,0 +1,81 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 1000, + "uom": "FT" + }, + "upperLimit": { + "value": 3500, + "uom": "FT" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.xml new file mode 100644 index 00000000..d47318bb --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_FT.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 3500 + STD + 1000 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.json new file mode 100644 index 00000000..3e6d5cf5 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.json @@ -0,0 +1,81 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 500, + "uom": "M" + }, + "upperLimit": { + "value": 3500, + "uom": "M" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.xml new file mode 100644 index 00000000..fed74f78 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_M.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 3500 + STD + 500 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.json new file mode 100644 index 00000000..9785ab0c --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2017-08-27T11:30:00Z" + }, + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "lowerLimit": { + "value": 35, + "uom": "FL" + }, + "lowerLimitOperator": "ABOVE", + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.xml new file mode 100644 index 00000000..a22e6b3b --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_OBSBEFORE.xml @@ -0,0 +1,109 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.json new file mode 100644 index 00000000..efe3dd14 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.json @@ -0,0 +1,81 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 0, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.xml new file mode 100644 index 00000000..b0ebb8a4 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_SFC_FL.xml @@ -0,0 +1,111 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 0 + SFC + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.json new file mode 100644 index 00000000..bb00bead --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "upperLimitOperator": "ABOVE", + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.xml new file mode 100644 index 00000000..021935aa --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPABV_FL.xml @@ -0,0 +1,110 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.json new file mode 100644 index 00000000..252f1240 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "upperLimitOperator": "BELOW", + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.xml new file mode 100644 index 00000000..da7edd6e --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOPBLW_FL.xml @@ -0,0 +1,110 @@ + + + + + 2018-08-27T11:40:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + + 35 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + 10.0 + 15.0 + + + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOP_FL.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOP_FL.json new file mode 100644 index 00000000..56849dfd --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_TOP_FL.json @@ -0,0 +1,77 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:40:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "EMBD_TS", + "analysisGeometries": [ + { + "intensityChange": "NO_CHANGE", + "movingDirection": { + "value": 10.0, + "uom": "deg" + }, + "movingSpeed": { + "value": 15.0, + "uom": "[kn_i]" + }, + "analysisType": "OBSERVATION", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.json new file mode 100644 index 00000000..ab093998 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.json @@ -0,0 +1,53 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "2", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "OBSC_TS", + "cancelledReference": { + "sequenceNumber": "2", + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T15:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.xml new file mode 100644 index 00000000..db70e5a3 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/sigmet_cancel.xml @@ -0,0 +1,70 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 2 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + 2 + + + 2018-08-27T15:00:00Z + 2018-08-27T18:00:00Z + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.json new file mode 100644 index 00000000..b821901e --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.json @@ -0,0 +1,123 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 53.0, + 5.0, + 54.0, + 6.0, + 55.0, + 4.0, + 53.0, + 5.0 + ] + } + } + } + ], + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + }, + "volcanoName": "GRIMSVOTN" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.xml new file mode 100644 index 00000000..2767896c --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1.xml @@ -0,0 +1,157 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + 53.0 5.0 54.0 6.0 55.0 4.0 53.0 5.0 + + + + + + + + + + + + + + + + + GRIMSVOTN + + + 52.0 5.2 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.json new file mode 100644 index 00000000..74e1e083 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.json @@ -0,0 +1,123 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 53.0, + 5.0, + 54.0, + 6.0, + 55.0, + 4.0, + 53.0, + 5.0 + ] + } + } + } + ], + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + }, + "volcanoName": "GRIMSVOTN" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.xml new file mode 100644 index 00000000..3cd03dff --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_OBSBEFORE.xml @@ -0,0 +1,157 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + 53.0 5.0 54.0 6.0 55.0 4.0 53.0 5.0 + + + + + + + + + + + + + + + + + GRIMSVOTN + + + 52.0 5.2 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.json new file mode 100644 index 00000000..eeaa81b5 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.json @@ -0,0 +1,73 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "2", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "cancelledReference": { + "sequenceNumber": "2", + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T15:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + } + }, + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + }, + "volcanoName": "GRIMSVOTN" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.xml new file mode 100644 index 00000000..6ba0f24f --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel.xml @@ -0,0 +1,70 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 2 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + 2 + + + 2018-08-27T15:00:00Z + 2018-08-27T18:00:00Z + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.json new file mode 100644 index 00000000..6be9d155 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.json @@ -0,0 +1,78 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "2", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "cancelledReference": { + "sequenceNumber": "2", + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T15:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "De BILT", + "type": "MWO", + "designator": "EHDB" + } + }, + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + }, + "volcanoName": "GRIMSVOTN" + }, + "volcanicAshMovedToFIR": { + "name": "BREMEN", + "type": "FIR", + "designator": "EDWW" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.xml new file mode 100644 index 00000000..0b499803 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_cancel_movtofir.xml @@ -0,0 +1,83 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 2 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + 2 + + + 2018-08-27T15:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + SNAPSHOT + FIR + EDWW + BREMEN + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.json new file mode 100644 index 00000000..8c0ac76a --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.json @@ -0,0 +1,108 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 53.0, + 5.0, + 54.0, + 6.0, + 55.0, + 4.0, + 53.0, + 5.0 + ] + } + } + } + ], + "VAInfo": { + "volcano": { + "volcanoName": "GRIMSVOTN" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.xml new file mode 100644 index 00000000..62465d98 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_nocoords.xml @@ -0,0 +1,150 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + 53.0 5.0 54.0 6.0 55.0 4.0 53.0 5.0 + + + + + + + + + + + + + + + + + GRIMSVOTN + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.json new file mode 100644 index 00000000..8a897575 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.json @@ -0,0 +1,122 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2018-08-27T18:00:00Z" + }, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 53.0, + 5.0, + 54.0, + 6.0, + 55.0, + 4.0, + 53.0, + 5.0 + ] + } + } + } + ], + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + } + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.xml new file mode 100644 index 00000000..23ff9b4b --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_noname.xml @@ -0,0 +1,157 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + + + + + + 53.0 5.0 54.0 6.0 55.0 4.0 53.0 5.0 + + + + + + + + + + + + + + + + + Unknown + + + 52.0 5.2 + + + + + diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.json b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.json new file mode 100644 index 00000000..e70069b0 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.json @@ -0,0 +1,103 @@ +{ + "reportStatus": "NORMAL", + "issuingAirTrafficServicesUnit": { + "name": "AMSTERDAM", + "type": "FIR", + "designator": "EHAA" + }, + "meteorologicalWatchOffice": { + "name": "DE BILT", + "type": "MWO", + "designator": "EHDB" + }, + "sequenceNumber": "1", + "issueTime": { + "completeTime": "2018-08-27T11:30:00Z" + }, + "airspace": { + "designator": "EHAA", + "name": "AMSTERDAM", + "type": "FIR" + }, + "validityPeriod": { + "startTime": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "endTime": { + "completeTime": "2018-08-27T18:00:00Z" + } + }, + "phenomenon": "VA", + "analysisGeometries": [ + { + "analysisType": "OBSERVATION", + "intensityChange": "NO_CHANGE", + "time": { + "completeTime": "2018-08-27T12:00:00Z" + }, + "lowerLimit": { + "value": 10, + "uom": "FL" + }, + "upperLimit": { + "value": 35, + "uom": "FL" + }, + "approximateLocation": false, + "geometry": { + "geoGeometry": { + "type": "Polygon", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "exteriorRingPositions": [ + 52.0, + 5.0, + 53.0, + 6.0, + 54.0, + 4.0, + 52.0, + 5.0 + ] + } + } + } + ], + "forecastGeometries": [ + { + "time": { + "completeTime": "2017-08-27T18:00:00Z" + }, + "approximateLocation": false, + "geometry": {}, + "noVolcanicAshExpected": true + } + ], + "VAInfo": { + "volcano": { + "volcanoPosition": { + "type": "ElevatedPoint", + "crs": { + "name": "http://www.opengis.net/def/crs/EPSG/0/4326", + "dimension": 2, + "axisLabels": [ + "Lat", + "Lon" + ] + }, + "coordinates": [ + 52.0, + 5.2 + ] + }, + "volcanoName": "GRIMSVOTN" + } + }, + "translated": false +} diff --git a/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.xml b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.xml new file mode 100644 index 00000000..c75ea814 --- /dev/null +++ b/src/test/resources/fi/fmi/avi/converter/iwxxm/v2023_1/vasigmet1_novaexp.xml @@ -0,0 +1,133 @@ + + + + + 2018-08-27T11:30:00Z + + + + + + + + SNAPSHOT + AMSTERDAM + FIC + EHAA + + + + + + + + + + SNAPSHOT + DE BILT + MWO + EHDB + + + + + + + + + + SNAPSHOT + FIR + EHAA + AMSTERDAM + + + + + 1 + + + 2018-08-27T12:00:00Z + 2018-08-27T18:00:00Z + + + + + + + + 2018-08-27T12:00:00Z + + + + + + + 35 + STD + 10 + STD + + + + + + + 52.0 5.0 53.0 6.0 54.0 4.0 52.0 5.0 + + + + + + + + + + + + + + + + + 2018-08-27T18:00:00Z + + + + + + + + GRIMSVOTN + + + 52.0 5.2 + + + + + +