/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eu.dnetlib.espas.util;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.util.GeometryEditor;
import eu.dnetlib.espas.util.GMLGeometryTransformationHandler.AddPointCoordinateEditorOperation.SUPPORTED_GEOMETRY;
import static eu.dnetlib.espas.util.GMLTransformationHandler.SUPPORT_GML_SPEC;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import org.apache.log4j.Logger;
import org.apache.log4j.Priority;
import org.geotoolkit.gml.xml.AbstractGeometry;
import org.geotoolkit.gml.xml.AbstractRingProperty;
import org.geotoolkit.gml.xml.Coordinates;
import org.geotoolkit.gml.xml.DirectPosition;
import org.geotoolkit.gml.xml.LinearRing;
import org.geotoolkit.gml.xml.Point;
import org.geotoolkit.gml.xml.Polygon;
import org.geotoolkit.gml.xml.Ring;
import org.geotoolkit.gml.xml.v321.AbstractGeometryType;
import org.geotoolkit.gml.xml.v321.AbstractSurfacePatchType;
import org.geotoolkit.gml.xml.v321.CoordinatesType;
import org.geotoolkit.gml.xml.v321.DirectPositionType;
import org.geotoolkit.gml.xml.v321.GeometryArrayPropertyType;
import org.geotoolkit.gml.xml.v321.LinearRingType;
import org.geotoolkit.gml.xml.v321.MultiGeometryType;
import org.geotoolkit.gml.xml.v321.PointArrayPropertyType;
import org.geotoolkit.gml.xml.v321.PointPropertyType;
import org.geotoolkit.gml.xml.v321.PointType;
import org.geotoolkit.gml.xml.v321.PolygonPatchType;
import org.geotoolkit.gml.xml.v321.PolygonType;
import org.geotoolkit.gml.xml.v321.RingType;
import org.geotoolkit.gml.xml.v321.SurfacePatchArrayPropertyType;
import org.geotoolkit.gml.xml.v321.SurfaceType;
import org.opengis.geometry.UnmodifiableGeometryException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


/** This class is responsible for handling the transformation of given GML representations into WKT representations that are fed to the ESPAS postgis 
 * db. The provided implementation is package visible and should not be further extended.
 * 
 * @author gathanas
 */
class GMLGeometryTransformationHandler extends GMLTransformationHandler {

    private static final double EARTH_EQUATORIAL_RADIUS = 6371.0 * 1000;
    private static final Logger _logger = Logger.getLogger(GMLGeometryTransformationHandler.class);


    public GMLGeometryTransformationHandler() {
        super();
    }

    /**
     * Returns the srs name assigned to the given geometry description. This in
     * ESPAS corresponds to the URI of the CRS system in the ESPAS Ontology.
     */
    String getGeometrySrsName(Node gmlNode) throws JAXBException {
        String result = "";

        if (!(gmlNode instanceof Element) || !((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC))
            return null;

        Unmarshaller unMarshaller = acquireUnMarshaller();

        Object gmlObject = unMarshaller.unmarshal(gmlNode);

        if (gmlObject instanceof JAXBElement)
            gmlObject = ((JAXBElement) gmlObject).getValue();
//        if this is not a geometry object then an empty result is returned
        if ((gmlObject instanceof AbstractGeometry))
            result = ((AbstractGeometry) gmlObject).getSrsName();

//      free resources by releasing unmashaller instance
        releaseUnMarshaller(unMarshaller);
        return result;
    }

    /** Returns the geometry representation of a simple gml:AbstractGeometry instance. 
     * Current implementation accommodates Point(s) and Polygon(s)
     */
    public String getGeometryRepresentation(Node gmlNode) throws JAXBException {
        String result = "";
        if (!(gmlNode instanceof Element) || !((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC))
            return null;

        _logger.debug("getGeography Representation: node is gml element");

        Unmarshaller unMarshaller = acquireUnMarshaller();

        Object gmlObject = unMarshaller.unmarshal(gmlNode);

        if (gmlObject instanceof JAXBElement)
            gmlObject = ((JAXBElement) gmlObject).getValue();
//        if this is not a geometry object then an emty result is returned

        _logger.debug("getGeography Representation: is gml object ::"+gmlObject.toString());

        if ((gmlObject instanceof AbstractGeometry))
            if (gmlObject instanceof Point)
                result = getPointGeoRepresentation((Point) gmlObject);
            else if (gmlObject instanceof Polygon)
                result = getPolygonGeoRepresentation((Polygon) gmlObject);

//      free resources by releasing unmashaller instance
        releaseUnMarshaller(unMarshaller);
        _logger.log(Priority.INFO,"GML geometry is :\n" + result);
        return result;
    }

    
/** This method returns a WKT representation of the bounding polygon, which includes all given points. It is assumed that all given gml 
 * node elements should be gml:Point(s) with the same CRS. Moreover, the version of the GML spec that is used for their specification should be 
 * v3.2.1.
 * The provided implementation takes care of inverted system definitions, i.e. systems that have inverted x & y coordinates. Returned WKT representations  
 * are 2D geometries for the given CRS system.
 * 
 */
    public String getBoundingPolygonRepresentation(Node[] gmlElements) {
        String result = "";
        try {
            Unmarshaller unMarshaller = acquireUnMarshaller();
            List<AbstractGeometryType> geometryList = new ArrayList<AbstractGeometryType>();           
//            traverse all comprising primitive nodes; these are assumed to be Point(s)
            
            for (Node gmlNode : gmlElements)                
                if ((gmlNode instanceof Element) && ((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC) && ((Element) gmlNode).getLocalName().equalsIgnoreCase("Point"))
                    try {
                        _logger.debug("getGeography Representation: node is gml Point element");

                        Object gmlObject = unMarshaller.unmarshal(gmlNode);
                        PointType source;
                        org.opengis.geometry.primitive.Point primObect;

                        GeometryEditor geoEditor = new GeometryEditor();
                        if (gmlObject instanceof JAXBElement)
                            source = (PointType) ((JAXBElement) gmlObject).getValue();
                        else
                            source = (PointType) gmlObject;
                        
                        PointType point = processPointType(null,(PointType)source);
                        
                        geometryList.add(point);
                        
                    } catch (Exception ex) {
                        _logger.error("Exception while processing gml element", ex);
                        continue;
                    }
                
                else if ((gmlNode instanceof Element) && ((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC) && ((Element) gmlNode).getLocalName().equalsIgnoreCase("MultiPoint")) {
                        _logger.debug("getGeography Representation: node is gml MultiPoint element");
                        
                        Object gmlObject = unMarshaller.unmarshal(gmlNode);
                        org.geotoolkit.gml.xml.v321.MultiPointType source;

                        GeometryEditor geoEditor = new GeometryEditor();
                        if (gmlObject instanceof JAXBElement)
                            source = (org.geotoolkit.gml.xml.v321.MultiPointType) ((JAXBElement) gmlObject).getValue();
                        else
                            source = (org.geotoolkit.gml.xml.v321.MultiPointType) gmlObject;
 
                        org.geotoolkit.gml.xml.v321.MultiPointType mPointType = source;
                        final CRSMapping pMapping = MetadataHandler.getSupportedCRSMapping(mPointType.getSrsName());
                        
                        if(mPointType.getPointMember()!=null)
                            for(PointPropertyType pointPropertyType: mPointType.getPointMember())
                                pointPropertyType.setPoint(this.processPointType(pMapping,pointPropertyType.getPoint()));
                        
                        else if(mPointType.getPointMembers()!=null){
                            PointArrayPropertyType pArrayType = mPointType.getPointMembers();
                            List<PointType> pointTypes = pArrayType .getPoint();
                            for(int l=0; l<pointTypes.size() ;l++)
                                pointTypes.set(l,this.processPointType(pMapping, pointTypes.get(l)));
                            mPointType.setPointMembers(pArrayType);
                        }                       
                        
                        geometryList.add(mPointType);
                }
                else if ((gmlNode instanceof Element) && ((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC)&& ((Element) gmlNode).getLocalName().equalsIgnoreCase("Polygon")) {
                        _logger.debug("getGeography Representation: node is gml MultiPoint element");
                        
                        Object gmlObject = unMarshaller.unmarshal(gmlNode);
                        org.geotoolkit.gml.xml.v321.PolygonType source;

                        GeometryEditor geoEditor = new GeometryEditor();
                        if (gmlObject instanceof JAXBElement)
                            source = (org.geotoolkit.gml.xml.v321.PolygonType) ((JAXBElement) gmlObject).getValue();
                        else
                            source = (org.geotoolkit.gml.xml.v321.PolygonType) gmlObject;
 
                        source = this.processPolygonType(null, source);
                        
                        geometryList.add(source);                        
                }
                else if ((gmlNode instanceof Element) && ((Element) gmlNode).getNamespaceURI().equalsIgnoreCase(SUPPORT_GML_SPEC)&& ((Element) gmlNode).getLocalName().equalsIgnoreCase("PolyhedralSurface")) {
                        _logger.debug("getGeography Representation: node is gml MultiPoint element");
                        
                        Object gmlObject = unMarshaller.unmarshal(gmlNode);
                        org.geotoolkit.gml.xml.v321.SurfaceType source;

                        GeometryEditor geoEditor = new GeometryEditor();
                        if (gmlObject instanceof JAXBElement)
                            source = (org.geotoolkit.gml.xml.v321.SurfaceType) ((JAXBElement) gmlObject).getValue();
                        else
                            source = (org.geotoolkit.gml.xml.v321.SurfaceType) gmlObject;
 
                        source = this.processPolyhedralSurface(null, source);
                        
                        geometryList.add(source);
                }                        
                else {
                    _logger.error("Error while processing gml representation. Either a not supported or not valid gml representation was provided ");
                    continue;
                }
            
            JAXBElement rootElement;            
            
            if(geometryList.size()>1){
                AbstractGeometryType geometryType = new MultiGeometryType();
                GeometryArrayPropertyType arrayType = new GeometryArrayPropertyType();
                for(AbstractGeometryType member: geometryList)
                    arrayType.getAbstractGeometry().add(new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,member.getClass().getSimpleName().replace("Type", "")),member.getClass(),member));
                ((MultiGeometryType)geometryType).setGeometryMembers(arrayType);                
                rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,"MultiGeometry"),MultiGeometryType.class,geometryType);
            }
            else{
                if (geometryList.get(0).getClass().getSimpleName().replace("Type", "").equalsIgnoreCase("Surface"))
                     rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,"PolyhedralSurface"),
                        geometryList.get(0).getClass(),geometryList.get(0));
                else
                rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,geometryList.get(0).getClass().getSimpleName().replace("Type", "")),
                        geometryList.get(0).getClass(),geometryList.get(0));
            }
            
//                serialize the content of the update polygon to retrieve the gml content that will be inserted in the db
            StringWriter strWriter = new StringWriter();
            Marshaller _marshaller = this.acquireMarshaller();
            _marshaller.marshal(rootElement, strWriter);
            releaseMarshaller(_marshaller);
            strWriter.flush();            
            result = strWriter.getBuffer().toString();            
        } catch (Exception ex) {
            _logger.error("Exception while processing gml element", ex);
        }
        
        finally {
            return result;
        }
    }

    
////////////////////////////////////////////////////////////////////////
//     Internal methods used for processing gml instances
    /**
     * This method checks if the used srs system has its x,y axis (i.e. long/lat) inverted compared to what postgis 
     * is expecting. Currently the GEI crs system defined for ESPAS has its coordinates inverted.
     */
   private boolean isInvertedSRSUsed(String srsName) throws GMLTransformationException{
        if(GeometryMetadataHandler.getSupportedCRSMapping(srsName)==null)
            throw new GMLTransformationException("Unsupported srs system :"+srsName);
        return GeometryMetadataHandler.getSupportedCRSMapping(srsName).isInvertedXY();
   }


   private int getSRIDCode(String srsName) {
        return GeometryMetadataHandler.getSupportedCRSMapping(srsName).getSrid();
   }
    
   private void processAbstractRing(AbstractRingProperty extRingProp, CRSMapping mapping) throws UnmodifiableGeometryException, IndexOutOfBoundsException {
      AddPointCoordinateEditorOperation pointEditor = new AddPointCoordinateEditorOperation(mapping, SUPPORTED_GEOMETRY._3D);

      if (extRingProp != null && extRingProp.getAbstractRing().getClass() == LinearRingType.class) {
//                    go through all pos or point references in the exterion LinearRing
         List<JAXBElement<?>> posOrPointList = ((LinearRingType) extRingProp.getAbstractRing()).getPosOrPointPropertyOrPointRep();
         for (JAXBElement posOrPoint : posOrPointList)
//                        if the element if the list is a direct position then just edit the coordinates and place the updated value in the element
            if (posOrPoint.getDeclaredType() == DirectPositionType.class)
               posOrPoint.setValue(pointEditor.editDirectPosition((DirectPositionType) posOrPoint.getValue()));
//                      if the element is a Point then update the internal pos coordinates and set the srs name to the one used throughout the polygon
            else if (posOrPoint.getDeclaredType() == PointType.class) {
               PointType pointT = (PointType) posOrPoint.getValue();
               pointT.setSrsName(mapping.getSridCode());
               pointT.setDirectPosition(pointEditor.editDirectPosition((DirectPosition) pointT.getPos()));
               posOrPoint.setValue(pointT);
            }
         
//             @todo: if the polygon is defined through coordinates then process the list of coordinates
         Coordinates cordinatesList = ((LinearRing) extRingProp.getAbstractRing()).getCoordinates();

      }      
      
//         @todo       process the exterion if this is a Ring and not a LinearRing
      else if(extRingProp!=null && extRingProp.getAbstractRing().getClass()== RingType.class){
         ((Ring)extRingProp.getAbstractRing()).getCurveMember();
      }
//                @todo need to process the internal rings as well 
   }

    
    /**
     */
   private String getPointGeoRepresentation( Point point) {
        _logger.debug("getPointGeoRepresentation: point ::"+point.toString());
        String result = "";
        try {
            point  = processPointType(null,(PointType)point);
            
            StringWriter strWriter = new StringWriter();
            JAXBElement rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,"Point"),PointType.class,(PointType)point);
            Marshaller _marshaller = this.acquireMarshaller();
            _marshaller.marshal(rootElement, strWriter);
            releaseMarshaller(_marshaller);
            strWriter.flush();            
            result = strWriter.getBuffer().toString();
            _logger.debug("getPointGeoRepresentation: result is ::"+result);
            
        } catch (Exception ex) {
            _logger.log(Priority.ERROR, null, ex);
        } finally {
            _logger.debug("getPointGeoRepresentation:: final is reached");
            return result;
        }
   }

   private PointType processPointType(CRSMapping pMapping, PointType point) throws UnmodifiableGeometryException, GMLTransformationException {
        int dimensions = point.getPos()!=null?point.getPos().getValue().size():point.getCoordinates().getValues().size();
//            makes sure that in case of two dimensional points the z vertice is set to null so as to support proper serialization to WKT format
        CRSMapping mapping = GeometryMetadataHandler.getSupportedCRSMapping(point.getSrsName());
        if(mapping==null)
            mapping=pMapping;
        AddPointCoordinateEditorOperation pointEditor = new AddPointCoordinateEditorOperation(mapping,SUPPORTED_GEOMETRY._3D);
        
        _logger.info("About to edit points !!");
        if(point.getPos()!=null){
            List<DirectPositionType> positionList = pointEditor.editDirectPositionList(Arrays.asList(new DirectPosition[]{point.getPos()}));
            point.setDirectPosition(positionList.get(0));
        }
        else{
            List<CoordinatesType> cordList = pointEditor.editCordinates(Arrays.asList(new Coordinates[]{point.getCoordinates()}));
            point.setCoordinates(cordList.get(0));
        }
        
        point.setSrsName(mapping.getSridCode());
        
        return point;
   }

   private String getPolygonGeoRepresentation( Polygon polygon) {
            _logger.debug("getPolygonGeoRepresentation: polygon is ::"+polygon.toString());
        String result = "";
        try {
           polygon= processPolygonType(null,polygon);
                
//                serialize the content of the update polygon to retrieve the gml content that will be inserted in the db
            StringWriter strWriter = new StringWriter();
            JAXBElement rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,"Polygon"),PolygonType.class,(PolygonType)polygon);
            Marshaller _marshaller = this.acquireMarshaller();
            _marshaller.marshal(rootElement, strWriter);
            releaseMarshaller(_marshaller);
            strWriter.flush();            
            result = strWriter.getBuffer().toString();
            
            _logger.debug("getPolygonGeoRepresentation: result is ::"+result);

        } catch (Exception ex) {
            _logger.log(Priority.ERROR, null, ex);
        }
        return result;
    }

   private PolygonType processPolygonType(CRSMapping pMapping, Polygon polygon) throws UnmodifiableGeometryException, IndexOutOfBoundsException {
        CRSMapping mapping = GeometryMetadataHandler.getSupportedCRSMapping(polygon.getSrsName());
        if(mapping==null)
            mapping=pMapping;
        
        
        AbstractRingProperty extRingProp= polygon.getExterior();
        processAbstractRing(extRingProp,mapping);
        
        return (PolygonType)polygon;
    }

   private String getPolyhedralSurfaceRepresentation(SurfaceType surface){
         _logger.debug("getPolygonGeoRepresentation: polygon is ::"+surface.toString());
        String result = "";
        try {
           surface= processPolyhedralSurface(null,surface);
//                serialize the content of the update surface to retrieve the gml content that will be inserted in the db
            StringWriter strWriter = new StringWriter();
            JAXBElement rootElement = new JAXBElement(new QName(MetadataHandler.GML32_NAMESPACE,"PolyhedralSurface"),SurfaceType.class,(SurfaceType)surface);
            Marshaller _marshaller = this.acquireMarshaller();
            _marshaller.marshal(rootElement, strWriter);
            releaseMarshaller(_marshaller);
            strWriter.flush();            
            result = strWriter.getBuffer().toString();
            
            _logger.debug("getPolyhedralSurfaceRepresentation: result is ::"+result);

        } catch (Exception ex) {
            _logger.log(Priority.ERROR, null, ex);
        }
        return result;
      
   }
    
   
//   processing of polyhedral surface types 
   private SurfaceType processPolyhedralSurface(CRSMapping pMapping,  SurfaceType polySurface){
       CRSMapping mapping = GeometryMetadataHandler.getSupportedCRSMapping(polySurface.getSrsName());
       if(mapping==null)
           mapping=pMapping;

       AddPointCoordinateEditorOperation pointEditor = new AddPointCoordinateEditorOperation(mapping,SUPPORTED_GEOMETRY._3D);
       SurfacePatchArrayPropertyType patchArray = polySurface.getPatches().getValue();
//        go through the list of supported patches
       for(JAXBElement patchJXB: patchArray.getAbstractSurfacePatch()){
//           the list of supported patches in a surface includes:
          if(patchJXB.getValue() instanceof PolygonPatchType){
//              process exterior ring of surface patch
             processAbstractRing(((PolygonPatchType)patchJXB.getValue()).getExterior(), mapping);
          }
          else
             ;

        }
      return  polySurface;
   }
    
   /**
     * Coordinate update editor which ensures that non-2d Points are transformed to 3d ones. This ensures that WKT serialization process will create
     * proper formats to be used in postgis insertion. This class is deeply internal to the point transformation code thus it is defined inside to the
     * corresponding operation
     */
   static class AddPointCoordinateEditorOperation implements GeometryEditor.GeometryEditorOperation {

       enum SUPPORTED_GEOMETRY {
           _2D, _3D
       };

       SUPPORTED_GEOMETRY geometry;
       private CRSMapping mapping;

       public AddPointCoordinateEditorOperation(CRSMapping mapping, SUPPORTED_GEOMETRY sGeometry) {
           this.geometry = sGeometry;
           this.mapping = mapping;
       }

       /**
        * Create another instance of the given point geometry which uses the updated set of coordinates as these are provided by the edit operation
        */
       @Override
       public Geometry edit(Geometry geometry, GeometryFactory factory) {
           _logger.debug("AddPointCoordinateEditorOperation: editing ::"+geometry.toText());
           if (factory == null)
               factory = geometry.getFactory();
           return factory.createPoint(edit(geometry.getCoordinates(), geometry)[0]);
       }

       /**
        * Invert the x (lat) and y (lon) ordinates so as to conform to the postgis expectations and set the z ordinate to 0 if it is not provided.
        */
       public Coordinate[] edit(Coordinate[] crdnts, Geometry gmtr) {
           if (gmtr instanceof com.vividsolutions.jts.geom.Point) {
               for (Coordinate cord : crdnts) {
                   if (mapping.isInvertedXY()) {
                       _logger.debug("AddPointCoordinateEditorOperation: inverting x-y axis for::"+gmtr.toText());
                       double hY = cord.x;
                       cord.x = cord.y;
                       cord.y = hY;
                   }
                   if (geometry.equals(SUPPORTED_GEOMETRY._3D) && Double.isNaN(cord.z))
                       cord.z = EARTH_EQUATORIAL_RADIUS;
               }
               return crdnts;
           }
           return crdnts;
       }

       /** edits a list of direct position coordinates according to the specifications set by the used CRS
        */        
       public List<DirectPositionType> editDirectPositionList(List<DirectPosition> directPositions) {
           List<DirectPositionType> updatedPositionList = new LinkedList<DirectPositionType>();
           for (DirectPosition cord : directPositions) {
               DirectPositionType positionType = editDirectPosition(cord);
               updatedPositionList.add(positionType);
           }
           return updatedPositionList;
       }

       /** edits the direct position coordinates according to the specifications set by the used CRS
        */
       public DirectPositionType editDirectPosition(DirectPosition cord) throws IndexOutOfBoundsException {
           DirectPositionType positionType;
           List<Double> posCords = new LinkedList<Double>();
           posCords.addAll(cord.getValue());
           CRSMapping m_mapping=MetadataHandler.getSupportedCRSMapping(cord.getSrsName());;
//            in case the enclosing GLM object doesn't have a mapping assigned then we should assume that all of the enclosed position elements will have one specified
           if(m_mapping==null)
               m_mapping = mapping;

           if (m_mapping.isInvertedXY()) {
               _logger.debug("AddPointCoordinateEditorOperation: inverting x-y axis ");
               posCords.set(0, cord.getOrdinate(1));
               posCords.set(1, cord.getOrdinate(0));
           }
           if (geometry.equals(SUPPORTED_GEOMETRY._3D) && cord.getValue().size()<3) {
               posCords.add(2, EARTH_EQUATORIAL_RADIUS * m_mapping.getzScaleFactor());
           }
           else if (geometry.equals(SUPPORTED_GEOMETRY._3D) && cord.getValue().size()==3) {
               posCords.set(2,  cord.getOrdinate(2)* m_mapping.getzScaleFactor());
           }
           positionType= new DirectPositionType(m_mapping.getSridCode(),3,posCords);
           return positionType;
       }

       public List<CoordinatesType> editCordinates(List<Coordinates> coordinates) {
           List<CoordinatesType> updatedPositionList = new LinkedList<CoordinatesType>();
               for (Coordinates cord : coordinates) {
                   String valueStr ="";

                   if (mapping.isInvertedXY()) {
                       _logger.debug("AddPointCoordinateEditorOperation: inverting x-y axis ");
                       List<Double> values= cord.getValues();
                       valueStr+=values.get(1)+","+values.get(0)+(values.size()>2?","+values.get(2):"");
                   }
                   if (geometry.equals(SUPPORTED_GEOMETRY._3D) && cord.getValues().size()<3)
                       valueStr+=","+EARTH_EQUATORIAL_RADIUS * mapping.getzScaleFactor(); 

                   CoordinatesType cordinatesType = new CoordinatesType();
                   cordinatesType.setValue(valueStr);
                   updatedPositionList.add(cordinatesType);
               }
               return updatedPositionList;
           }

   }
}

