[GIS] Read WKT geometry using JTS 1.13 in Java

geotoolsjavajavascriptjts-topology-suiteopenlayers

I want to create a shapefile from coordinates, in the client side I take the coordinates of a polygon by OpenLayers, it sends it to a Java class, in this class I read the geometry of the polygon, but when the geometry is read, the method shows this exception:

java.lang.IllegalArgumentException: Points of LinearRing do not form a closed linestring

I'm using this code:

Here I take the geometry of the vector:

 .....
    {
        var featureGeom = [];
        var arreglo = [];
        var arreglo2 = [];

        var controlador = aplicacion.getController("ControlResultados");        
        var vectorSeleccion = controlador.buscarcapa('capaSeleccion').getSource().getFeatures();

        for (var x = 0; x < vectorSeleccion.length; x++) {
            featureGeom.push(vectorSeleccion[x].getGeometry().getCoordinates());
            for (var y = 0; y < featureGeom[x][0].length; y++) {
                arreglo.push(featureGeom[x][0][y]);
            }
        }

        for (var z = 0; z < arreglo.length; z++) {
            arreglo2.push(arreglo[z][0]+" "+arreglo[z][1]);
        }

        var nombreArchivo = "archivoSHPResultado";
        var parametros = {
                archivoPersistencia : nombreArchivo,
                geomPolygon : "POLYGON(("+arreglo2.toString()+"))",
                nombreArchivo: 'CapaSHPResultado'
        };
        this.descargaArchivoSHP(parametros);
    },
    descargaArchivoSHP: function(contenido){
        Ext.Ajax.request({
            url : "ServletShape",
            method : 'POST',
            params : contenido,
            success : function( respuesta ) {
                var rutaActual = location.href.split("//");
                var rutaActualPartes = rutaActual[1].split("/");
                var nuevaRuta = rutaActual[0] + "//" + rutaActualPartes[0] + "/" + rutaActualPartes[1] + "/" + respuesta.responseText;

                window.open(nuevaRuta);

                Ext.MessageBox.show({
                    title: 'WARN',
                    msg: 'OK',
                    buttonText: {
                        yes: 'OK',
                    }
                });
            },
            failure : function() {

                Ext.MessageBox.show({
                    title: 'WARN',
                    msg: 'FAIL',
                    buttonText: {
                        yes: 'OK',
                    }
                });
            }
        }); 
  }
  ......

Here's the Java class:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.geotools.data.DataUtilities;
import org.geotools.data.DefaultTransaction;
import org.geotools.data.Transaction;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.DefaultFeatureCollection;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.opengis.feature.simple.SimpleFeatureType;

import utilidades.utilidadesGeometriaGeotools;

import com.vividsolutions.jts.geom.Geometry;

public class BBShape {

private static final int BUFFER = 4096;
private String rutaPersistencia = null; 
private String nombreArchivoZip = null;
private String nombreXlsSalida = null;

public String getRutaPersistencia() {
 return rutaPersistencia;
}

public void setRutaPersistencia(String rutaPersistencia) {
  this.rutaPersistencia = rutaPersistencia;
}

public String getNombreArchivoZip() {
  return nombreArchivoZip;
}

public void setNombreArchivoZip(String nombreArchivoZip) {
this.nombreArchivoZip = nombreArchivoZip;
}

public void Exportar(String nombreShape, String tipoShape, String geomShape, String separador){

try {

    String pathArchivo = rutaPersistencia + nombreShape + ".shp";

    SimpleFeatureType featureType =  DataUtilities.createType( tipoShape,  "location:" + tipoShape + ":srid=4326," + "number:Integer");

    DefaultFeatureCollection featureCollection = new DefaultFeatureCollection("internal",featureType);

    String[] geomArray = geomShape.split(separador);

    for (int i = 0; i < geomArray.length; i++) {
        Geometry geometria = utilidadesGeometriaGeotools.WKTgeometriaLectura(geomArray[i]);
        featureCollection.add( SimpleFeatureBuilder.build( featureType, new Object[]{ geometria, 2}, null) );
    }

    File file = new File(pathArchivo);  

    ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();

    Map<String, Serializable> params = new HashMap<String, Serializable>();
    params.put("url", file.toURI().toURL());
    params.put("create spatial index", Boolean.TRUE);

    ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
    newDataStore.createSchema(featureType);
    newDataStore.forceSchemaCRS(DefaultGeographicCRS.WGS84);

    Transaction transaction = new DefaultTransaction("create");

    String typeName = newDataStore.getTypeNames()[0];
    SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName);

    if (featureSource instanceof SimpleFeatureStore) {
        SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;

        featureStore.setTransaction(transaction);
        try {
            featureStore.addFeatures(featureCollection);
            transaction.commit();

        } catch (Exception problem) {
            problem.printStackTrace();
            transaction.rollback();

        } finally {
            transaction.close();
        }
    } else {
        System.out.println(typeName + " No se peude acceder");
    }

} catch (Exception e1) {

}   
}

public void deleteFolderContent(File folder){
  File[] files = folder.listFiles();
  if(files!=null)
    for(File f: files)
        if(f.isDirectory()) deleteFolderContent(f);
        else f.delete();
}

public String zipDirectorio() {

String pathCarpeta= rutaPersistencia;
String pathArchivo = pathCarpeta + nombreArchivoZip + ".zip";

File d = new File(pathCarpeta);
if (!d.isDirectory())
    throw new IllegalArgumentException(pathCarpeta + " no es un directorio." );
String[] entries = d.list();

byte[] buffer = new byte[BUFFER];
int bytesRead;

try {
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(pathArchivo));

    for (int i = 0; i < entries.length; i++) {
        File f = new File(d, entries[i]);

        if (f.isDirectory())
            continue; 

        FileInputStream in = new FileInputStream(f);

        ZipEntry entry = new ZipEntry(entries[i]);
        out.putNextEntry(entry);

        while ((bytesRead = in.read(buffer)) != -1)
            out.write(buffer, 0, bytesRead);
        in.close();
    }
    out.close();

} catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

nombreXlsSalida = this.getNombreArchivoZip() + ".zip";

return (nombreXlsSalida);
}

public String  exportarOpcionesSHP(String pathLogico, String ruta, String geomPolygon, String nombre){

this.setRutaPersistencia(ruta);

String strPathFile = "";
File f=new File(ruta);
f.mkdir();

if(f.exists()){
    this.deleteFolderContent(f);

    if (!geomPolygon.equals("")) this.Exportar("poligonos", "Polygon", geomPolygon, "%,");
}

this.setNombreArchivoZip(nombre);
strPathFile = this.zipDirectorio();

strPathFile = pathLogico + strPathFile;
return strPathFile;
}   
}

Here read the geometry

package utilidades;

import java.io.IOException;
import java.io.StringWriter;

import org.geotools.geometry.jts.JTS;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.referencing.CRS;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;

import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;

import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;
import com.vividsolutions.jts.io.WKTWriter;

public class utilidadesGeometriaGeotools {

static public String WKTgeometria(Geometry geom){

StringWriter writer = new StringWriter();
WKTWriter wktWriter = new WKTWriter(2);

try {
    wktWriter.write( geom, writer );
} catch (IOException e) {
}
String wkt = writer.toString();
return wkt;
}
static public Geometry WKTgeometriaLectura(String geom){
Geometry geometria = null;        
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
WKTReader reader = new WKTReader(geometryFactory);

try {
    geometria = (Geometry) reader.read(geom);

} catch (ParseException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
return geometria;
}

 static public LineString  construirLinea (Double xIni,Double yIni,Double xFin,Double yFin ){    
return  (LineString) WKTgeometriaLectura("LINESTRING("+xIni+" "+yIni+","+ xFin+" "+yFin+")");
}

static public Geometry transformacion(Geometry geom,String sistemaOrigen,String sistemaDestino ){

Geometry salidaGeometry = null; 
try {               
    CoordinateReferenceSystem sourceCRS = CRS.decode(sistemaOrigen);//CRS.decode("EPSG:4326");
    CoordinateReferenceSystem targetCRS = CRS.decode(sistemaDestino); //CRS.decode("EPSG:3116");    
    MathTransform   transform = CRS.findMathTransform(sourceCRS, targetCRS, false);
    salidaGeometry = JTS.transform( geom, transform);

} catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
return salidaGeometry;
}
}

Best Answer

You need to close the polygon by adding the start point to the end of the list of points that you build the WKT with. So change it to:

    for (var z = 0; z < arreglo.length; z++) {
        arreglo2.push(arreglo[z][0]+" "+arreglo[z][1]);
    }
    arreglo2.push(arreglo[0][0]+" "+arreglo[0][1]);

But I'm pretty sure you can use the OpenLayers WKT functionality

    WKT.write(layer.selectedFeatures[0]); 

to do the same thing more easily.

Or if you are using many polygons, I would go straight to GeoJSON up and use the GeoTools GeoJSON Datastore to read it into a feature collection and then write it out as a Shapefile.