org.n52.ses.io.parser.FaaSaaPilotParser.java Source code

Java tutorial

Introduction

Here is the source code for org.n52.ses.io.parser.FaaSaaPilotParser.java

Source

/**
 * Copyright (C) 2012
 * by 52 North Initiative for Geospatial Open Source Software GmbH
 *
 * Contact: Andreas Wytzisk
 * 52 North Initiative for Geospatial Open Source Software GmbH
 * Martin-Luther-King-Weg 24
 * 48155 Muenster, Germany
 * info@52north.org
 *
 * This program is free software; you can redistribute and/or modify it under
 * the terms of the GNU General Public License version 2 as published by the
 * Free Software Foundation.
 *
 * This program is distributed WITHOUT ANY WARRANTY; even without the implied
 * WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program (see gnu-gpl v2.txt). If not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA or
 * visit the Free Software Foundation web page, http://www.fsf.org.
 */
package org.n52.ses.io.parser;

import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;

import net.opengis.gml.x32.AbstractTimePrimitiveType;
import net.opengis.gml.x32.BoundingShapeType;
import net.opengis.gml.x32.CodeWithAuthorityType;
import net.opengis.gml.x32.EnvelopeType;
import net.opengis.gml.x32.TimePeriodType;
import net.opengis.gml.x32.TimePositionType;
import net.opengis.gml.x32.TimePrimitivePropertyType;

import org.apache.muse.ws.notification.NotificationMessage;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.DateTimeFormatterBuilder;
import org.n52.ses.api.AbstractParser;
import org.n52.ses.api.event.MapEvent;
import org.n52.ses.api.exception.GMLParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Element;

import aero.aixm.schema.x51.AbstractAIXMFeatureType;
import aero.aixm.schema.x51.AirspaceActivationPropertyType;
import aero.aixm.schema.x51.AirspaceActivationType;
import aero.aixm.schema.x51.AirspaceLayerType;
import aero.aixm.schema.x51.AirspaceTimeSliceType;
import aero.aixm.schema.x51.AirspaceType;
import aero.aixm.schema.x51.CodeAirspaceDesignatorType;
import aero.aixm.schema.x51.event.EventType;
import aero.aixm.schema.x51.message.AIXMBasicMessageDocument;
import aero.aixm.schema.x51.message.BasicMessageMemberAIXMPropertyType;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;

/**
 * A parser for AIXM updates as provided in the 
 * OGC FAA SAA Dissemination Pilot
 * 
 * @author Thomas Everding
 *
 */
public class FaaSaaPilotParser extends AbstractParser {

    private static final Logger logger = LoggerFactory.getLogger(FaaSaaPilotParser.class);

    //AIXM namespace
    private static final String AIXM_MESSAGE_NAMESPACE = "http://www.aixm.aero/schema/5.1/message";

    //AIXM Basic Message QName
    private static final QName MESSAGE_ROOT_QNAME = new QName(AIXM_MESSAGE_NAMESPACE, "AIXMBasicMessage");

    //UCUM code for foot
    private static final String UCUM_FEET = "[ft_i]";

    private DateTimeFormatter fmt = this.buildDTFormatter();

    @Override
    public boolean accept(NotificationMessage message) {
        Element elem = message.getMessageContent(MESSAGE_ROOT_QNAME);
        if (elem != null) {
            return true;
        }
        return false;
    }

    @Override
    public List<MapEvent> parse(NotificationMessage message) throws Exception {
        List<MapEvent> result = new ArrayList<MapEvent>();

        //get the notification content and create XMLBeans representation
        Element elem = message.getMessageContent(MESSAGE_ROOT_QNAME);
        AIXMBasicMessageDocument aixmMessage = AIXMBasicMessageDocument.Factory.parse(elem, null);

        result.add(this.parseAIXMBasicMessage(aixmMessage));

        return result;
    }

    /**
     * parses an AIXM basic message
     * 
     * @param aixmMessage the message
     * @return the message parse in a {@link MapEvent}
     */
    private MapEvent parseAIXMBasicMessage(AIXMBasicMessageDocument aixmMessage) {
        MapEvent mapEvent = new MapEvent(0, 0);

        //get hasMember elements
        BasicMessageMemberAIXMPropertyType[] members = aixmMessage.getAIXMBasicMessage().getHasMemberArray();

        for (BasicMessageMemberAIXMPropertyType member : members) {
            this.parseMember(member, mapEvent);
        }

        return mapEvent;
    }

    /**
     * Parses a member of the AIXM Basic Message
     * 
     * @param member the member
     * @param mapEvent the event to populate
     */
    private void parseMember(BasicMessageMemberAIXMPropertyType member, MapEvent mapEvent) {
        AbstractAIXMFeatureType abtractAIXMFeature = member.getAbstractAIXMFeature();

        if (abtractAIXMFeature instanceof EventType) {
            //parse the Event part
            EventType event = (EventType) abtractAIXMFeature;
            this.parseEvent(event, mapEvent);
        } else if (abtractAIXMFeature instanceof AirspaceType) {
            //parse the Airspace part
            AirspaceType airspace = (AirspaceType) abtractAIXMFeature;
            this.parseAirspace(airspace, mapEvent);
        }
    }

    /**
     * Parses an airspace
     * 
     * @param airspace the airspace type
     * @param mapEvent the event to be populated
     */
    private void parseAirspace(AirspaceType airspace, MapEvent mapEvent) {
        //parse the identifier
        if (airspace.isSetIdentifier()) {
            CodeWithAuthorityType identifier = airspace.getIdentifier();
            this.parseGMLIdentifier(identifier, mapEvent);
        }

        //parse the time slice
        if (airspace.getTimeSliceArray().length > 0) {
            AirspaceTimeSliceType airspaceTimeSlice = airspace.getTimeSliceArray(0).getAirspaceTimeSlice();
            this.parseTimeSlice(airspaceTimeSlice, mapEvent);
        }

        if (airspace.isSetBoundedBy()) {
            //parse bounding box
            this.parseBoundingBox(airspace.getBoundedBy(), mapEvent);
        }
    }

    /**
     * parses an airspace time slice
     * 
     * @param airspaceTimeSlice the time slice node of an airspace
     * @param mapEvent the map event to populate
     */
    private void parseTimeSlice(AirspaceTimeSliceType airspaceTimeSlice, MapEvent mapEvent) {
        //parse valid time
        this.parseValidTime(airspaceTimeSlice.getValidTime(), mapEvent);

        //parse activation
        if (airspaceTimeSlice.getActivationArray().length > 0) {
            AirspaceActivationPropertyType activation = airspaceTimeSlice.getActivationArray(0);
            this.parseActivation(activation.getAirspaceActivation(), mapEvent);
        }

        //parse designator
        if (airspaceTimeSlice.isSetDesignator()) {
            this.parseDesignator(airspaceTimeSlice.getDesignator(), mapEvent);
        }

    }

    /**
     * Parses the designator
     * 
     * @param designator the designator of an airspacetiemslice
     * @param mapEvent the map event to populate
     */
    private void parseDesignator(CodeAirspaceDesignatorType designator, MapEvent mapEvent) {
        // parse the designator value
        String designatorValue = designator.getStringValue();
        mapEvent.put(MapEvent.AIXM_DESIGNATOR_KEY, designatorValue);
    }

    /**
     * Parses the activation of the time slice
     * 
     * @param activation the aixm:activation element
     * @param mapEvent the map event to populate
     */
    private void parseActivation(AirspaceActivationType activation, MapEvent mapEvent) {
        //parse status
        if (activation.isSetStatus()) {
            String status = activation.getStatus().getStringValue();
            mapEvent.put(MapEvent.STAUS_KEY, status);
        }

        //parse levels
        if (activation.getLevelsArray().length > 0) {
            AirspaceLayerType levels = activation.getLevelsArray(0).getAirspaceLayer();
            this.parseLevels(levels, mapEvent);
        }

        //      //parse extension
        //      if (activation.getExtensionArray().length > 0) {
        //         Extension extension = activation.getExtensionArray(0);
        //         this.parseActivationExtension(extension, mapEvent);
        //      }
    }

    //   /**
    //    * parses an aixm:extension element of the activation
    //    * 
    //    * @param extension the extension element
    //    * @param mapEvent the event to populate
    //    */
    //   private void parseActivationExtension(Extension extension, MapEvent mapEvent) {
    //      //get the reservation phase element
    //      AbstractExtensionType ext = extension.getAbstractAirspaceActivationExtension();
    //      if (ext instanceof AirspaceActivationExtensionType) {
    //         AirspaceActivationExtensionType aaEx = (AirspaceActivationExtensionType) ext;
    //         
    //         if (aaEx.isSetReservationPhase()) {
    //            String phase = aaEx.getReservationPhase().toString();
    //            mapEvent.put(MapEvent.RESERVATION_PHASE_KEY, phase);
    //         }
    //      }
    //   }

    /**
     * Parses the levels of an activation
     * 
     * @param layer the aixm:levels element
     * @param mapEvent the event to populate
     */
    private void parseLevels(AirspaceLayerType layer, MapEvent mapEvent) {
        if (!layer.isSetLowerLimit() || !layer.isSetUpperLimit()) {
            return;
        }

        //get values and convert
        double u = Double.parseDouble(layer.getUpperLimit().getStringValue());
        double l = Double.parseDouble(layer.getLowerLimit().getStringValue());

        //convert lower to SI unit
        String uom = "";
        if (layer.getLowerLimit().isSetUom()) {
            uom = layer.getLowerLimit().getUom();

            //adjust non-UCUM codes
            if (uom.equals("FT") || uom.equals("ft")) {
                uom = UCUM_FEET;
            }
        }
        l = this.unitConverter.convert(uom, l).getValue();

        //convert upper to SI unit
        uom = "";
        if (layer.getUpperLimit().isSetUom()) {
            uom = layer.getUpperLimit().getUom();

            //adjust non-UCUM codes
            if (uom.equals("FT") || uom.equals("ft")) {
                uom = UCUM_FEET;
            }
        }
        u = this.unitConverter.convert(uom, u).getValue();

        //store level information in event
        mapEvent.put(MapEvent.LOWER_LIMIT_KEY, l);
        mapEvent.put(MapEvent.UPPER_LIMIT_KEY, u);
    }

    /**
     * Parses the valid time of the time slice
     * 
     * @param timePrimitivePropertyType the gml:validTime element
     * @param mapEvent the map event to populate
     */
    private void parseValidTime(TimePrimitivePropertyType timePrimitivePropertyType, MapEvent mapEvent) {
        long begin = 0;
        long end = 0;

        //parse the valid time element
        AbstractTimePrimitiveType abstractTime = timePrimitivePropertyType.getAbstractTimePrimitive();
        if (abstractTime instanceof TimePeriodType) {
            //parse gml:TimePeriod type
            TimePeriodType timePeriod = (TimePeriodType) abstractTime;
            if (timePeriod.isSetBeginPosition()) {
                begin = this.parseTimePosition(timePeriod.getBeginPosition());
            }
            if (timePeriod.isSetEndPosition()) {
                end = this.parseTimePosition(timePeriod.getEndPosition());
            }
        }
        //parse other time types here

        mapEvent.put(MapEvent.START_KEY, begin);
        mapEvent.put(MapEvent.END_KEY, end);
    }

    /**
     * Parses a gml:identifier element
     * 
     * @param identifier the gml:identifier
     * @param mapEvent the map event to populate
     */
    private void parseGMLIdentifier(CodeWithAuthorityType identifier, MapEvent mapEvent) {
        //get value
        String value = identifier.getStringValue();

        //get code space
        String codeSpace = identifier.getCodeSpace();

        //set properties
        mapEvent.put(MapEvent.IDENTIFIER_CODESPACE_KEY, codeSpace);
        mapEvent.put(MapEvent.IDENTIFIER_VALUE_KEY, value);
    }

    /**
     * Parses an event:Event into a {@link MapEvent}
     * 
     * @param event the AIXM Event to parse
     * 
     * @param mapEvent the map event for the results
     */
    private void parseEvent(EventType event, MapEvent mapEvent) {
        if (event.isSetBoundedBy()) {
            //parse the bounding box of the event
            this.parseBoundingBox(event.getBoundedBy(), mapEvent);
        }
    }

    /**
     * parses a bounding box
     * 
     * @param boundingBox the gml Bounding box
     * @param mapEvent the event to populate
     */
    private void parseBoundingBox(BoundingShapeType boundingBox, MapEvent mapEvent) {
        //check if a bounding box was already parsed (multiple instances possible)
        if (mapEvent.containsKey(MapEvent.GEOMETRY_KEY)) {
            //geometry already parsed and set
            return;
        }

        //geometry not yet parsed, do so
        EnvelopeType envelope = boundingBox.getEnvelope();
        try {
            Geometry geom = GML32Parser.parseGeometry(envelope);
            mapEvent.put(MapEvent.GEOMETRY_KEY, geom);
        } catch (ParseException e) {
            logger.warn(e.getMessage(), e);
        } catch (GMLParseException e) {
            logger.warn(e.getMessage(), e);
        }
    }

    /**
     * Parses a time position
     * 
     * @param timePositionType
     * @return
     */
    private long parseTimePosition(TimePositionType timePositionType) {
        String timeString = timePositionType.getStringValue();

        //replace Z by +00:00
        if (timeString.endsWith("Z")) {
            timeString = timeString.substring(0, timeString.length() - 1) + "+00:00";
        }

        DateTime position = this.fmt.parseDateTime(timeString);
        return position.getMillis();
    }

    /**
     * 
     * @return a formatter for common ISO strings
     */
    private DateTimeFormatter buildDTFormatter() {
        //build a parser for time stamps
        return new DateTimeFormatterBuilder().appendYear(4, 4) //4 digit year (YYYY)
                .appendLiteral("-").appendMonthOfYear(2) //2 digit month (MM)
                .appendLiteral("-").appendDayOfMonth(2) //2 digit day (DD)
                .appendLiteral("T").appendHourOfDay(2) //2 digit hour (hh)
                .appendLiteral(":").appendMinuteOfHour(2) //2 digit minute (mm)
                .appendLiteral(":").appendSecondOfMinute(2)//2 digit second (ss)
                //optional 3 digit milliseconds of second
                .appendOptional(
                        new DateTimeFormatterBuilder().appendLiteral(".").appendMillisOfSecond(3).toParser())
                //optional time zone offset as (+|-)hh:mm
                .appendOptional(new DateTimeFormatterBuilder().appendTimeZoneOffset("", true, 2, 2).toParser())
                .toFormatter();
    }

    @Override
    protected String getName() {
        return "FAA SAA Pilot parser";
    }

    /**
     * test main
     * @param args s
     */
    public static void main(String[] args) {
        //      System.out.println("initialize()");
        //      
        //      Logger logger = Logger.getLogger(FaaSaaPilotParser.class.getName());
        //      
        //      System.out.println("logger built");
        //      
        //      IUnitConverter uConv = new SESUnitConverter(logger);
        //      
        //      System.out.println("unit conv built");
        //      
        //      String testFilePath = "./ses-main/src/main\\resources\\wsdl\\test_files\\EVENT_2328231_PENDING.xml";
        //      File testFile = new File(testFilePath);
        //      
        //      System.out.println("file found: " + testFile.exists());
        //      
        //      try {
        //         ConfigurationRegistry.init(new FileInputStream(testFile), logger, null, uConv);
        //      }
        //      catch (FileNotFoundException e) {
        //         e.printStackTrace();
        //      }
        //      
        //      FaaSaaPilotParser parser = new FaaSaaPilotParser(uConv);
        //      
        //      System.out.println("parser built");
        //      
        //      AIXMBasicMessageDocument message = null;
        //      try {
        //         message = AIXMBasicMessageDocument.Factory.parse(testFile);
        //      }
        //      catch (XmlException e) {
        //         e.printStackTrace();
        //      }
        //      catch (IOException e) {
        //         e.printStackTrace();
        //      }
        //      
        //      if (message != null) {
        //         System.out.println("test input parsed to XML bean");
        //         System.out.println(message.toString());
        //         
        //         MapEvent event = parser.parseAIXMBasicMessage(message);
        //         
        //         System.out.println(event.toString());
        //      }
    }
}