XMLNetworkWriter.java

/**
 *
 */
package org.opentrafficsim.road.network.factory.vissim;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.djunits.unit.DirectionUnit;
import org.djunits.unit.LengthUnit;
import org.djunits.unit.SpeedUnit;
import org.djunits.value.vdouble.scalar.Length;
import org.djunits.value.vdouble.scalar.Speed;
import org.opentrafficsim.core.geometry.OTSPoint3D;
import org.opentrafficsim.core.gtu.GTUType;
import org.opentrafficsim.core.network.NetworkException;
import org.opentrafficsim.road.network.factory.vissim.xsd.DEFINITIONS;
import org.opentrafficsim.road.network.factory.vissim.xsd.GLOBAL;
import org.opentrafficsim.road.network.factory.vissim.xsd.GTU;
import org.opentrafficsim.road.network.factory.vissim.xsd.LINK;
import org.opentrafficsim.road.network.factory.vissim.xsd.LINK.BEZIER;
import org.opentrafficsim.road.network.factory.vissim.xsd.NETWORK;
import org.opentrafficsim.road.network.factory.vissim.xsd.NODE;
import org.opentrafficsim.road.network.factory.vissim.xsd.ObjectFactory;
import org.opentrafficsim.road.network.factory.vissim.xsd.ROADLAYOUT;
import org.opentrafficsim.road.network.factory.vissim.xsd.ROADTYPE;
import org.opentrafficsim.road.network.lane.Lane;
import org.opentrafficsim.road.network.lane.object.LaneBasedObject;
import org.opentrafficsim.road.network.lane.object.sensor.SingleSensor;

/**
 * @author NLGUTU
 */
public class XMLNetworkWriter
{

    static void writeToXML(File file, Map<String, LinkTag> linkTags, Map<String, NodeTag> nodeTags, String sinkKillClassName,
            String sensorClassName, String trafficLightName) throws NetworkException
    {
        try
        {

            DEFINITIONS definitions = generateDefinitions();

            generateGtusAndRoadtypes(definitions);

            List<NODE> nodes = new ArrayList<>();
            generateNodes(nodes, nodeTags);

            List<LINK> links = new ArrayList<>();
            List<ROADLAYOUT> roadLayouts = new ArrayList<>();
            generateLinks(links, roadLayouts, linkTags, sinkKillClassName, sensorClassName, trafficLightName);
            definitions.getContent().addAll(roadLayouts);

            marshall(file, definitions, nodes, links);

        }
        catch (JAXBException e)
        {
            e.printStackTrace();
        }
    }

    private static DEFINITIONS generateDefinitions()
    {
        DEFINITIONS definitions = new DEFINITIONS();
        generateGTUTypes(definitions);
        GLOBAL global = new GLOBAL();
        definitions.getContent().add(global);
        return definitions;
    }

    private static void generateGTUTypes(DEFINITIONS definitions)
    {
        List<GTUTYPE> gtuTypes = new ArrayList<>();
        GTUTYPE gtuType = new GTUTYPE();
        gtuType.setNAME("CAR");
        gtuTypes.add(gtuType);
        definitions.getContent().addAll(gtuTypes);
    }

    private static void generateGtusAndRoadtypes(DEFINITIONS definitions)
    {
        // definitions.getContent().add(gtuType);
        List<GTU> gtus = new ArrayList<>();
        GTU gtu = new GTU();
        gtu.setNAME("CAR");
        gtu.setGTUTYPE("CAR");
        gtu.setMAXSPEED("CONST(" + new Speed(140, SpeedUnit.KM_PER_HOUR).getInUnit(SpeedUnit.KM_PER_HOUR) + ") km/h");
        gtu.setLENGTH("CONST(" + new Length(4.5, LengthUnit.METER).getInUnit(LengthUnit.METER) + ") m");
        gtu.setWIDTH("CONST(" + new Length(2.0, LengthUnit.METER).getInUnit(LengthUnit.METER) + ") m");
        gtus.add(gtu);
        definitions.getContent().addAll(gtus);

        // road types
        List<ROADTYPE> roadTypes = new ArrayList<>();
        ROADTYPE roadType = new ROADTYPE();
        roadType.setDEFAULTLANEKEEPING("KEEPLANE");
        roadType.setDEFAULTOVERTAKING("NONE");
        roadType.setDEFAULTLANEWIDTH("3.5m");
        roadType.setNAME("RINGROAD");
        ROADTYPE.SPEEDLIMIT speedLimit = new ROADTYPE.SPEEDLIMIT();
        speedLimit.setGTUTYPE(gtu.getGTUTYPE());
        speedLimit.setLEGALSPEEDLIMIT(new Speed(140, SpeedUnit.KM_PER_HOUR).getInUnit(SpeedUnit.KM_PER_HOUR) + " km/h");
        roadType.getSPEEDLIMIT().add(speedLimit);
        roadTypes.add(roadType);
        definitions.getContent().addAll(roadTypes);

    }

    private static void marshall(File file, DEFINITIONS definitions, List<NODE> nodes, List<LINK> links) throws JAXBException
    {
        JAXBContext jaxbContext = JAXBContext.newInstance("org.opentrafficsim.road.network.factory.vissim.xsd");
        Marshaller marshaller = jaxbContext.createMarshaller();
        ObjectFactory outputFactory = new ObjectFactory();
        NETWORK networkElement = outputFactory.createNETWORK();
        networkElement.getDEFINITIONSOrIncludeOrNODE().add(definitions);
        networkElement.getDEFINITIONSOrIncludeOrNODE().addAll(nodes);
        networkElement.getDEFINITIONSOrIncludeOrNODE().addAll(links);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(networkElement, System.out);
        marshaller.marshal(networkElement, file);
    }

    private static void generateLinks(List<LINK> links, List<ROADLAYOUT> roadLayouts, Map<String, LinkTag> linkTags,
            String sinkKillClassName, String sensorClassName, String trafficLightName) throws NetworkException
    {

        Iterator<LinkTag> iter = linkTags.values().iterator();
        while (iter.hasNext())
        {
            LinkTag inputLink = iter.next();
            LINK link = new LINK();
            // set link the attributes and items
            link.setNAME(inputLink.name);
            String layoutName = "rl" + link.getNAME();
            link.setROADLAYOUTAttribute(layoutName);

            if (inputLink.arcTag != null)
            {
                LINK.ARC arc = new LINK.ARC();
                arc.setANGLE(inputLink.arcTag.angle.getInUnit(DirectionUnit.EAST_DEGREE) + " deg");
                arc.setRADIUS("" + inputLink.arcTag.radius.getInUnit(LengthUnit.METER));
                arc.setDIRECTION("" + inputLink.arcTag.direction);
                link.setARC(arc);
            }

            if (inputLink.bezierTag != null)
            {
                LINK.BEZIER bezier = new BEZIER();
                link.setBEZIER(bezier);
            }

            ROADLAYOUT rla = new ROADLAYOUT();
            rla.setROADTYPE("RINGROAD");
            rla.setNAME(layoutName);

            Iterator<Lane> lanes = inputLink.lanes.values().iterator();
            while (lanes.hasNext())
            {
                ROADLAYOUT.LANE lane = new ROADLAYOUT.LANE();
                Lane inputLane = lanes.next();
                lane.setNAME(inputLane.getId());
                lane.setWIDTH(inputLane.getBeginWidth().getInUnit(LengthUnit.METER) + "m");
                lane.setOFFSET(inputLane.getDesignLineOffsetAtBegin().getInUnit(LengthUnit.METER) + "m");
                if (inputLane.getDesignLineOffsetAtBegin().ne(inputLane.getDesignLineOffsetAtEnd()))
                {
                    double differenceOffset = inputLane.getDesignLineOffsetAtEnd().minus(inputLane.getDesignLineOffsetAtBegin())
                            .getInUnit(LengthUnit.METER);
                    link.setOFFSETEND("" + differenceOffset + "m");
                }
                if (inputLink.connector)
                {
                    lane.setCOLOR("BLACK");
                }
                else
                {
                    lane.setCOLOR("GRAY");
                }
                lane.setDIRECTION("FORWARD");
                ROADLAYOUT.LANE.SPEEDLIMIT speedLimit = new ROADLAYOUT.LANE.SPEEDLIMIT();
                try
                {
                    speedLimit.setLEGALSPEEDLIMIT(
                            inputLane.getSpeedLimit(GTUType.VEHICLE).getInUnit(SpeedUnit.KM_PER_HOUR) + " km/h");
                }
                catch (Exception exception)
                {
                    System.err.println(exception.getMessage());
                    speedLimit.setLEGALSPEEDLIMIT("100.0 km/h");
                }
                speedLimit.setGTUTYPE("CAR");
                lane.getSPEEDLIMIT().add(speedLimit);
                rla.getLANEOrNOTRAFFICLANEOrSHOULDER().add(lane);
                for (SingleSensor inputSensor : inputLane.getSensors())
                {
                    LINK.SENSOR sensor = new LINK.SENSOR();
                    sensor.setNAME(inputSensor.getId());
                    sensor.setLANE(lane.getNAME());
                    sensor.setPOSITION(
                            Double.toString(inputSensor.getLongitudinalPosition().getInUnit(LengthUnit.METER)) + " m");
                    sensor.setTRIGGER(" " + inputSensor.getPositionType());
                    if (sensor.getNAME().startsWith("SINK@"))
                    {
                        sensor.setCLASS(sinkKillClassName);
                    }
                    else
                    {
                        sensor.setCLASS(sensorClassName);
                    }
                    link.getLANEOVERRIDEOrGENERATOROrLISTGENERATOR().add(sensor);
                }

                for (LaneBasedObject inputSimpleTrafficLight : inputLane.getLaneBasedObjects())
                {
                    LINK.TRAFFICLIGHT simpleTrafficLight = new LINK.TRAFFICLIGHT();
                    simpleTrafficLight.setNAME(inputSimpleTrafficLight.getId());
                    simpleTrafficLight.setLANE(lane.getNAME());
                    simpleTrafficLight.setPOSITION(
                            Double.toString(inputSimpleTrafficLight.getLongitudinalPosition().getInUnit(LengthUnit.METER))
                                    + " m");
                    simpleTrafficLight.setCLASS(trafficLightName);
                    link.getLANEOVERRIDEOrGENERATOROrLISTGENERATOR().add(simpleTrafficLight);
                }
                ROADLAYOUT.STRIPE stripe = new ROADLAYOUT.STRIPE();
                stripe.setTYPE("DASHED");
                stripe.setOFFSET(inputLane.getDesignLineOffsetAtBegin().minus(inputLane.getBeginWidth().divideBy(2.0))
                        .getInUnit(LengthUnit.METER) + "m");
                rla.getLANEOrNOTRAFFICLANEOrSHOULDER().add(stripe);

            }
            // link.setROADLAYOUT(rla);
            roadLayouts.add(rla);

            if (inputLink.straightTag != null)
            {
                LINK.STRAIGHT straight = new LINK.STRAIGHT();
                if (inputLink.straightTag.length != null)
                {
                    straight.setLENGTH(inputLink.straightTag.length.getInUnit(LengthUnit.METER) + " m");
                }
                link.setSTRAIGHT(straight);
            }

            if (inputLink.polyLineTag != null)
            {
                LINK.POLYLINE polyLine = new LINK.POLYLINE();
                String coordString = null;
                int length = inputLink.polyLineTag.vertices.length;
                for (int i = 0; i < length; i++)
                {
                    OTSPoint3D coord = inputLink.polyLineTag.vertices[i];
                    coordString = "(" + coord.x + "," + coord.y + "," + coord.z + ")";
                    polyLine.getINTERMEDIATEPOINTS().add(coordString);
                }
                link.setPOLYLINE(polyLine);
            }

            link.setNODESTART(inputLink.nodeStartTag.name);
            link.setNODEEND(inputLink.nodeEndTag.name);
            links.add(link);
        }
    }

    private static void generateNodes(List<NODE> nodes, Map<String, NodeTag> nodeTags)
    {
        Iterator<NodeTag> iterNode = nodeTags.values().iterator();
        while (iterNode.hasNext())
        {
            NodeTag inputNode = iterNode.next();
            NODE node = new NODE();
            node.setNAME(inputNode.name);
            node.setCOORDINATE(inputNode.coordinate.toString());
            nodes.add(node);
        }
    }

}