content: /**
   * ﻿Copyright (C) 2007 - 2019 52°North Initiative for Geospatial Open Source
   * Software GmbH
   *
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 as published
   * by the Free Software Foundation.
   *
   * If the program is linked with libraries which are licensed under one of
   * the following licenses, the combination of the program with the linked
   * library is not considered a "derivative work" of the program:
   *
   *       • Apache License, version 2.0
   *       • Apache Software License, version 1.0
   *       • GNU Lesser General Public License, version 3
   *       • Mozilla Public License, versions 1.0, 1.1 and 2.0
   *       • Common Development and Distribution License (CDDL), version 1.0
   *
   * Therefore the distribution of the program linked with libraries licensed
   * under the aforementioned licenses, is permitted by the copyright holders
   * if the distribution is compliant with both the GNU General Public
   * License version 2 and the aforementioned licenses.
   *
   * As an exception to the terms of the GPL, you may copy, modify,
   * propagate, and distribute a work formed by combining 52°North WPS
   * GeoTools Modules with the Eclipse Libraries, or a work derivative of
   * such a combination, even if such copying, modification, propagation, or
   * distribution would otherwise violate the terms of the GPL. Nothing in
   * this exception exempts you from complying with the GPL in all respects
   * for all of the code used other than the Eclipse Libraries. You may
   * include this exception and its grant of permissions when you distribute
   * 52°North WPS GeoTools Modules. Inclusion of this notice with such a
   * distribution constitutes a grant of such permissions. If you do not wish
   * to grant these permissions, remove this paragraph from your
   * distribution. "52°North WPS GeoTools Modules" means the 52°North WPS
   * modules using GeoTools functionality - software licensed under version 2
   * or any later version of the GPL, or a work based on such software and
   * licensed under the GPL. "Eclipse Libraries" means Eclipse Modeling
   * Framework Project and XML Schema Definition software distributed by the
   * Eclipse Foundation and licensed under the Eclipse Public License Version
   * 1.0 ("EPL"), or a work based on such software and licensed under the EPL.
   *
   * This program is distributed in the hope that it will be useful, but
   * WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
   * Public License for more details.
   */
  package org.n52.wps.io.datahandler.generator;
  
  import java.io.ByteArrayInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  import org.geotools.feature.FeatureCollection;
  import org.geotools.feature.FeatureIterator;
  import org.n52.wps.io.data.GenericFileDataWithGT;
  import org.n52.wps.io.data.IData;
  import org.n52.wps.io.data.binding.complex.GTVectorDataBinding;
  import org.n52.wps.io.data.binding.complex.GenericFileDataWithGTBinding;
  import org.opengis.feature.simple.SimpleFeature;
  
  import com.vividsolutions.jts.geom.Coordinate;
  import com.vividsolutions.jts.geom.Geometry;
  import com.vividsolutions.jts.geom.LineString;
  import com.vividsolutions.jts.geom.MultiLineString;
  import com.vividsolutions.jts.geom.MultiPolygon;
  import com.vividsolutions.jts.geom.MultiPoint;
  import com.vividsolutions.jts.geom.Point;
  import com.vividsolutions.jts.geom.Polygon;
  
  /**
   * 
   * This generator generates JSON Geometries (points, polylines, polygons,
   * see this website: 
   * http://help.arcgis.com/en/webapi/javascript/arcgis/help/jsapi_start.htm)
   * @author Merret Buurman, 52 North / ifgi
   * June 2012
   * 
   * So far, only one Geometry can be handled at a time (no collections).
   * 
   * Geometries can be created from from Shapefiles. Support for GTVectorBindings is
   * almost ready, but not complete yet.
   * 
   * GeometryCollections: Each Geometry will be transformed to a JSON geometry separately,
   * they will be put in an array [geometry, geometry, geometry]
   * 
   * MultiLineStrings and MultiPolygons:
   * These geometries are not defined in http://help.arcgis.com/en/webapi/javascript/
   * arcgis/help/jsapi_start.htm, only Point, Polyline, Polygon and MultiPoint are defined.
   * MultiLineStrings and MultiPolygons are (by default) transformed to normal Polygons 
   * and Polylines, having multiple "paths"/"rings".
   * As an alternative, MultiLineStrings and MultiPolygons can be handled as several 
   * linestrings / polygons in an array (MultiLineString => [linestring, linestring, 
   * linestring], etc.). For this, set "multiGeometriesToArray" to false.
   *
   */
  
  public class JSONGeometryGenerator extends AbstractGenerator {
  	
  	private static Logger LOGGER = LoggerFactory.getLogger(JSONGeometryGenerator.class); 
  	
  	boolean isShapefile = false; // needed for a workaround: if the input is gml without a specified crs,
  								 // the coordinates usually are swapped, compared to the shapefile
  	
  	boolean multiGeometriesToArray = false; // if you prefer the multipolygons or multilinestrings as an 
  										    // array [polygon, polygon, polygon], set this to true. If you prefer
  										    // them as multiple rings/paths in one linestring or polygon, set it to
  										    // false
  	
  	
  	
  	public JSONGeometryGenerator(){
  		super();
  		//supportedIDataTypes.add(GTVectorDataBinding.class);
  		supportedIDataTypes.add(GenericFileDataWithGTBinding.class);
  	}
  
  	@Override
  	public InputStream generateStream(IData data, String mimeType, String schema)
  			throws IOException {
  		
  		LOGGER.info("Starting to generate json geometry out of the process result");
  		
  		// Extract input data (as feature collection)
  		GTVectorDataBinding gvdb = null;
  		if (data instanceof GenericFileDataWithGTBinding){
  			LOGGER.debug("The data passed from the algorithm to the generator is GenericFileDataBinding");
  			try {
  				gvdb = ((GenericFileDataWithGT) data.getPayload()).getAsGTVectorDataBinding();
  				isShapefile = true;
  			} catch (Exception e){
  				throw new IOException("The data passed from the algorithm to the generator is a file, but no shapefile");
  			}
  		} else if (data instanceof GTVectorDataBinding){
  			LOGGER.debug("The data passed from the algorithm to the generator is GTVectorDataBinding");
  			gvdb = (GTVectorDataBinding) data;
  			isShapefile = false;
  		} else {
  			throw new IOException("The data passed from the algorithm to the generator has to be a file (shapefile)!");
  		}
  		FeatureCollection fc = gvdb.getPayload();
  		
  		if(fc == null || fc.size() == 0) {
  			throw new IOException("No feature was passed to the generator!");
  		}
  		
  		// Transform features to JSON
  		FeatureIterator fi = fc.features();
  		String jsonString = "";
  		if (fc.size() == 1){
  			// only one feature: make a simple json geometry
  			SimpleFeature f = (SimpleFeature) fi.next();
  			Geometry geom = this.getGeometry(f);
  			jsonString = this.transformOneFeature(geom);
  		} else {
  			// several features: put the geometries into an array
  			jsonString = "[";
  			while (fi.hasNext()) {
  				SimpleFeature f = (SimpleFeature) fi.next();
  				Geometry geom = this.getGeometry(f);
  				String oneFeatureString = this.transformOneFeature(geom);
  				jsonString = jsonString.concat(oneFeatureString + ", ");
  			}
  			jsonString = jsonString.concat("]");
  		}
  		
  		// Make stream out of it and return stream
  		InputStream is = new ByteArrayInputStream(jsonString.getBytes());
  		return is;
  	}
  	
  	
  	
  	
  	
  	
  	/**
  	 * Get and return a feature's geometry.
  	 * @param SimpleFeature
  	 * @return Geometry
  	 * @throws IOException
  	 */
  	private Geometry getGeometry(SimpleFeature simpleFeature) throws IOException {
  		
  		Geometry geom = null;
  		if(simpleFeature.getDefaultGeometry()==null && simpleFeature.getAttributeCount()>0 
  				&& simpleFeature.getAttribute(0) instanceof Geometry){
  			geom = (Geometry)simpleFeature.getAttribute(0);
  		}else{
  			geom = (Geometry)simpleFeature.getDefaultGeometry();
  		}
  		if ((geom == null) || !(geom instanceof Geometry)){
  			LOGGER.error("Geometry could not be extracted");
  			throw new IOException("Geometry could not be extracted!");
  		}
  		LOGGER.debug("Geometry extracted");
  		return geom;
  		
  	}
  	
  	/**
  	 * Transform a feature to a JSON string
  	 * 
  	 * The feature that is passed to this method is transformed to a JSON string according
  	 * to this site (http://help.arcgis.com/en/webapi/javascript/arcgis/help/jsapi_start.htm).
  	 * 
  	 * @param geom
  	 * @return
  	 * @throws IOException
  	 */
  	private String transformOneFeature(Geometry geom) throws IOException {
  		
  		// Depending on geometry's type, transform to json geometry
  		String jsonString = "";
  		
  		// simple geometries:
  		if (geom instanceof Point){
  			jsonString = transformPointToJsonPoint((Point) geom);
  		} else if (geom instanceof LineString){
  			jsonString = transformLineStringToJsonLineString((LineString) geom);
  		} else if (geom instanceof Polygon){
  			jsonString = transformPolygonToJsonPolygon((Polygon) geom);
  		}
  		
  		// multipoint
  		else if (geom instanceof MultiPoint){
  			MultiPoint multipoint = (MultiPoint) geom;
  			
  			// if only one point inside, make normal point out of it
  			if (multipoint.getNumGeometries() == 1){
  				Point point = (Point) multipoint.getGeometryN(0);
  				jsonString = transformPointToJsonPoint(point);
  			} else {
  				jsonString = transformMultiPointToJsonMultiPoint(multipoint);
  			}
  			
  		
  		// multilinestring
  		} else if (geom instanceof MultiLineString){
  			MultiLineString multiline = (MultiLineString) geom;
  			
  			// if only one linestring inside, make normal linestring out of it
  			if (multiline.getNumGeometries() == 1){
  				LineString line = (LineString) multiline.getGeometryN(0);
  				jsonString = transformLineStringToJsonLineString(line);
  			}
  			// real multilinestring
  			else {
  				if (multiGeometriesToArray){
  					jsonString = transformMultiLineStringToJsonLineStringArray(multiline);
  				} else {
  					jsonString = transformMultiLineStringToJsonLineString(multiline);
  				}
  			}
  			
  		
  		// multipolygon
  		} else if (geom instanceof MultiPolygon){
  			MultiPolygon multipoly = (MultiPolygon) geom;
  			
  			// if only one polygon inside, make normal polygon out of it
  			if (multipoly.getNumGeometries() == 1){
  				Polygon poly = (Polygon) multipoly.getGeometryN(0);
  				jsonString = transformPolygonToJsonPolygon(poly);
  			}
  			
  			// real multi polygon
  			else {
  				if (multiGeometriesToArray){
  					jsonString = transformMultiPolygonToJsonPolygonArray(multipoly);
  				} else {
  					jsonString = transformMultiPolygonToJsonPolygon(multipoly);
  				}
  				
  			}
  		} else {
  			LOGGER.error("Feature has no recognized geometry type");
  		}
  		
  		return jsonString;
  	}
  	
  
  	
  	/*
  	 * Private methods that do the actual transformation:
  	 * 
  	 */
  	
  	
  	/* *** Transformations for points *** */
  	// Simple point
  	private String transformPointToJsonPoint(Point p){
  		
  		LOGGER.info("Transforming point to JSON point.");
  		
  		/* Points are like this: {"x": -122.65, "y": 45.53, "spatialReference": {" wkid": 4326 } }
  		 * We will assemble it in five parts:
  		 * 		(1)  {
  		 * 		(2)  "x": -122.65,
  		 * 		(3)  "y": 45.53,
  		 * 		(4)  "spatialReference": {" wkid": 4326 }
  		 * 		(5)  }
  		 */
  
  		// Get coordinates
  		Coordinate coordinatePair = p.getCoordinate();
  		double x = coordinatePair.x;
  		double y = coordinatePair.y;
  		LOGGER.debug("Point's coordinates: " + x + ", " + y);
  		
  		// Define which one is east-west and which one is north-south
  		// TODO There should not be this difference!! I think the error is that 
  		// x and y were wrongly given to GML input!
  		double eastwest;
  		double northsouth;
  		if (isShapefile){
  			// In GTVectorBindings made of shapefiles, 
  			// the x seems to stand for north-south
  			// and the y seems to stand for east-west
  			eastwest = x;
  			northsouth = y;
  		} else {
  			// In GML content (without given CRS)
  			// the y seems to stand for north-south
  			// and the x seems to stand for east-west
  			northsouth = x;
  			eastwest = y;
  		}
  		
  		// Get CRS, part (4)
  		String wkid;
  		try {
  			wkid = crsToJsonSpatialReference(p);
  		} catch (IOException e) {
  			wkid = "\"spatialReference\":{\"wkid\":0000}";
  		}
  		LOGGER.info("Point's CRS: " + wkid);
  		
  		// Assemble json geometry point
  		/* In jsongeometry, x stands for EAST/WEST coordinate, y for NORTH/SOUTH. */
  		String jsonString = "{\"x\": " + eastwest + ", \"y\": " + northsouth + ", " + wkid +"}";
  		LOGGER.info("Finished JSON point: " + jsonString);
  		
  		return jsonString;
  	}
  	// Multipoint
  	private String transformMultiPointToJsonMultiPoint(MultiPoint multipoint){
  		
  		LOGGER.info("Transforming multipoint to json multipoint");
  		
  		/* Multipoints are like this:
  		 * {"points":[[-122.63,45.51],[-122.56,45.51],[-122.56,45.55]],"spatialReference":({" wkid":4326 })}
  		 * 		(1)   {"points":[
  		 * 		(2)   [coords],[coords],[coords],
  		 * 		(4)   ]
  		 * 		(5)   "spatialReference":{" wkid":4326 }
  		 * 		(6)   }
  		 * */
  		
  		// Get CRS in this form: "spatialReference":{" wkid":4326 }
  		String wkid;
  		try {
  			wkid = crsToJsonSpatialReference(multipoint);
  		} catch (IOException e) {
  			wkid = "\"spatialReference\":{\"wkid\":0000}";
  		}
  		LOGGER.debug("Point's CRS: " + wkid);
  		
  		// Step (1)
  		String jsonString = "{\"points\":[";
  		
  		// Step (2)
  		boolean firstElement = true;
  		for (int i = 0; i < multipoint.getNumGeometries(); i++){
  			
  			/* Treat one point of the multipoint: */
  			Point point = (Point) multipoint.getGeometryN(i);
  			String onePointString = "[";
  			// Get coordinates
  			Coordinate coordinatePair = point.getCoordinate();
  			double x = coordinatePair.x;
  			double y = coordinatePair.y;
  			LOGGER.info("Point's coordinates: " + x + ", " + y);
  			// Define which one is east-west and which one is north-soutn
  			// TODO There should not be this difference!! I think the error is that 
  			// x and y were wrongly given to GML input!
  			double eastwest;
  			double northsouth;
  			if (isShapefile){
  				// In GTVectorBindings made of shapefiles, 
  				// the x seems to stand for north-south
  				// and the y seems to stand for east-west
  				eastwest = x;
  				northsouth = y;
  			} else {
  				// In GML content (without given CRS)
  				// the y seems to stand for north-south
  				// and the x seems to stand for east-west
  				northsouth = x;
  				eastwest = y;
  			}
  			/* In JSONGEOMETRY, x stands for EAST/WEST coordinate, y for NORTH/SOUTH.
  			 * Lines are like this [eastwest, northsouth] */
  			onePointString = onePointString.concat(eastwest + "," + northsouth + "]");
  			
  			if (firstElement){
  				jsonString = jsonString.concat(onePointString);
  				firstElement = false;
  			} else {
  				jsonString = jsonString.concat("," + onePointString);
  			}
  		}
  		// Step (3), (4), (5)
  		jsonString = jsonString.concat("], " + wkid + "}");
  		
  		return jsonString;
  	}	
  	
  	/* *** Transformations for lines *** */
  	// Simple linestring
  	private String transformLineStringToJsonLineString(LineString linestring){
  		
  		LOGGER.info("Transforming linestring to JSON linestring");
  		
  		/* Lines are like this: {"paths":[[[-122.68,45.53], [-122.58,45.55],[-122.57,45.58],[-122.53,45.6]]],
  		"spatialReference":{"wkid":4326}}
  		* For a simple line, we put only one path.
  		* We will assemble it in several parts:
  		* 		(a)  {"paths":[
  		* 		(b)  one path, like this: [  [coords],[coords],[coords]  ]  (it has square brackets)
  		*       (c)  ],
  		*       (d)  "spatialReference":{"wkid":4326}
  		*       (e)  }
  		*/
  		
  		// (1) make path
  		String aPath = linestringToJsonPath(linestring); // like this: [  [coords],[coords],[coords]  ]
  		
  		// (2) assemble to line
  		String aJsonLine = assembleLine(aPath, linestring);
  		
  		return aJsonLine;
  	}
  	// Multilinestring
  	private String transformMultiLineStringToJsonLineString(MultiLineString multilinestring){
  		
  		LOGGER.info("Transforming multilinestring to JSON linestring with several paths in it.");
  		
  		/* Lines are like this: {"paths":[[[-122.68,45.53], [-122.58,45.55],[-122.57,45.58],[-122.53,45.6]]],
  		"spatialReference":{"wkid":4326}}
  		* For a multi line, we put several paths.
  		* We will assemble it in several parts:
  		* 		(a)  {"paths":[
  		* 		(b)  several paths, comma separated, each has square brackets:
  		* 		(b)  		[  [coords],[coords],[coords]  ]   <== this is one path, with square brackets!
  		* 		(b)  		[  [coords],[coords],[coords]  ],  <== this is one path, with square brackets!
  		* 		(b)  		[  [coords],[coords],[coords]  ],  <== this is one path, with square brackets!
  		*       (c)  ],
  		*       (d)  "spatialReference":{"wkid":4326}
  		*       (e)  }
  		*/
  		
  		// (1) make paths, and concatenate them, comma separated
  		String allPaths = "";
  		boolean firstElement = true;
  		for (int i = 0; i < multilinestring.getNumGeometries(); i++){
  			LineString linestring = (LineString) multilinestring.getGeometryN(i);
  			String aPath = linestringToJsonPath(linestring);
  			if (firstElement){
  				allPaths = allPaths.concat(aPath);
  				firstElement = false;
  			} else {
  				allPaths = allPaths.concat(", " + aPath);
  			}
  		}
  		
  		// (2) assemble to line
  		String jsonString = assembleLine(allPaths, multilinestring);
  		return jsonString;
  	}
  	private String transformMultiLineStringToJsonLineStringArray(MultiLineString multilinestring){
  		
  		LOGGER.info("Transforming multilinestring to JSON linestring array");
  		
  		/* 
  		 * For a multi line, we make several simple lines and put them into an array
  		 */
  		
  		String lineArray = "[";
  		boolean firstElement = true;
  		for (int i = 0; i < multilinestring.getNumGeometries(); i++){
  			LineString linestring = (LineString) multilinestring.getGeometryN(i);
  			String aLine = transformLineStringToJsonLineString(linestring);
  			if (firstElement){
  				lineArray = lineArray.concat(aLine);
  				firstElement = false;
  			} else {
  				lineArray = lineArray.concat(", " + aLine);
  			}
  		}
  		
  		return lineArray;
  	}
  	// Helpers
  	private String assembleLine(String aPathOrSeveralPaths, Geometry geometry){
  		
  		/* Lines are like this: {"paths":[[[-122.68,45.53], [-122.58,45.55],[-122.57,45.58],[-122.53,45.6]]],
  		"spatialReference":{"wkid":4326}}
  		
  		* We will assemble it in several parts:
  		* 		(a)  {"paths":[
  		* 		(b)  either a path or several paths (comma separated)
  		*       (c)  ],
  		*       (d)  "spatialReference":{"wkid":4326}
  		*       (e)  }
  		*/
  		
  		// (1) make spatial reference
  		String spatialReference;
  		try {
  			spatialReference = crsToJsonSpatialReference(geometry);
  		} catch (IOException e) {
  			spatialReference = "\"spatialReference\":{\"wkid\":0000}";
  		}
  		
  		// (2) Assemble json geometry polyline
  		String jsonString = "{\"paths\":[" + aPathOrSeveralPaths + "]," + spatialReference + "}";
  		return jsonString;
  		
  		
  	}
  	private String linestringToJsonPath(LineString ls){
  		
  		/* A path is like this: 
  		 * 	[
  		 * 		[-122.68,45.53], [-122.58,45.55],[-122.57,45.58],[-122.53,45.6]
  		 * 	]
  		 * 
  		 * It is returned WITH the surrounding square brackets!
  		 */
  		
  		// Iterate over all coordinate pairs in the linestring
  		Coordinate[] coordinateArray = ls.getCoordinates();
  		String aPath = "[";
  		boolean firstElement = true;
  		for (Coordinate coordinatePair : coordinateArray){
  			
  			// Assign x and y to eastwest or northeast, depending on input
  			// TODO There should not be this difference!! I think the error is that 
  			// x and y were wrongly given to GML input!
  			double eastwest = 0.0;
  			double northsouth = 0.0;
  			if (isShapefile){
  				// In GTVectorBindings made of shapefiles, 
  				// the x seems to stand for north-south
  				// and the y seems to stand for east-west
  				eastwest = coordinatePair.x;
  				northsouth = coordinatePair.y;
  			} else {
  				// In GML content (without given CRS)
  				// the y seems to stand for north-south
  				// and the x seems to stand for east-west
  				eastwest = coordinatePair.y;
  				northsouth = coordinatePair.x;
  			}
  			
  			// Make coordinate pair string
  			String coordinatePairString = "";
  			/* In JSONGEOMETRY, x stands for EAST/WEST coordinate, y for NORTH/SOUTH.
  			 * Lines are like this [eastwest, northsouth] */
  			if (firstElement){
  				coordinatePairString = "[" + eastwest + "," + northsouth + "]";
  				firstElement = false;
  			} else {
  				coordinatePairString = ", [" + eastwest + "," + northsouth + "]";
  			}
  			
  			// Stick all coordinate pair strings together
  			aPath = aPath.concat(coordinatePairString);
  		}
  		aPath = aPath.concat("]");
  		return aPath;
  		
  	}
  	
  	/* *** Transformations for polygons *** */
  	// Simple polygon
  	private String transformPolygonToJsonPolygon(Polygon polygon){
  		
  		LOGGER.info("Transforming simple polygon to JSON polygon");
  		
  		/* Polygons are like this:
  		 * {"rings":[[[-122.63,45.52],[-122.57,45.53],[-122.52,45.50],[-122.49,45.48],
    		   [-122.64,45.49],[-122.63,45.52],[-122.63,45.52]]],"spatialReference":{" wkid":4326 }}
  		 * 
  		 * For a simple polygon, we take only one ring:
  		 * 
  		 * 		(a) {"rings":[
  		 * 		(b) a ring, like this: [[coords],[coords],[coords]]    <== this is one ring, with square brackets
  		 * 		(c) ],
  		 * 		(d) "spatialReference":{" wkid":4326 }
  		 * 		(e) }
  		 */
  		
  		// (1) make ring
  		String aRing = getPathFromPolygon(polygon);
  		
  		
  		// (2) assemble polygon
  		String jsonString = assemblePolygon(aRing, polygon);
  		return jsonString;
  		
  		
  	}
  	// Multipolygon
  	private String transformMultiPolygonToJsonPolygon(MultiPolygon multipolygon){
  		
  	
  		LOGGER.info("Transforming multipolygon to JSON polygon with several rings in it.");
  		
  		/* Lines are like this: {"paths":[[[-122.68,45.53], [-122.58,45.55],[-122.57,45.58],[-122.53,45.6]]],
  		"spatialReference":{"wkid":4326}}
  		* For a multi polygon, we put several paths.
  		* We will assemble it in several parts:
  		* 		(a)  {"paths":[
  		* 		(b)  several paths, comma separated, each has square brackets:
  		* 		(b)  		[  [coords],[coords],[coords]  ]   <== this is one path, with square brackets!
  		* 		(b)  		[  [coords],[coords],[coords]  ],  <== this is one path, with square brackets!
  		* 		(b)  		[  [coords],[coords],[coords]  ],  <== this is one path, with square brackets!
  		*       (c)  ],
  		*       (d)  "spatialReference":{"wkid":4326}
  		*       (e)  }
  		*/
  		
  		// (1) make paths, and concatenate them, comma separated
  		String allRings = "";
  		boolean firstElement = true;
  		for (int i = 0; i < multipolygon.getNumGeometries(); i++){
  			Polygon polygon = (Polygon) multipolygon.getGeometryN(i);
  			String aRing = getPathFromPolygon(polygon);
  			if (firstElement){
  				allRings = allRings.concat(aRing);
  				firstElement = false;
  			} else {
  				allRings = allRings.concat(", " + aRing);
  			}
  		}
  		
  		// (2) assemble polygon
  		String jsonString = assemblePolygon(allRings, multipolygon);
  		return jsonString;
  	}
  	private String transformMultiPolygonToJsonPolygonArray(MultiPolygon multipolygon){
  		
  		LOGGER.info("Transforming multipolygon to JSON polygon array");
  		
  		/* 
  		 * For a multi polygon, we make several simple polygons and put them into an array
  		 */
  		
  		String polygonArray = "[";
  		boolean firstElement = true;
  		for (int i = 0; i < multipolygon.getNumGeometries(); i++){
  			Polygon polygon = (Polygon) multipolygon.getGeometryN(i);
  			String aPolygon = transformPolygonToJsonPolygon(polygon);
  			if (firstElement){
  				polygonArray = polygonArray.concat(aPolygon);
  				firstElement = false;
  			} else {
  				polygonArray = polygonArray.concat(", " + aPolygon);
  			}
  		}
  		
  		return polygonArray;
  	}
  	// Helpers
  	private String getPathFromPolygon(Polygon polygon){
  		
  		// Make a path (=ring) from a simple polygon
  		
  		// Does the polygon have holes?
  		int holes = polygon.getNumInteriorRing();
  		if (holes < 0){
  			LOGGER.info("The polygon that is being transformed to an json geometry " +
  					"has holes. Holes are not accepted. Only the outer ring will be " +
  					"transformed.");
  		}
  		
  		// Get the outer ring
  		LineString outer = polygon.getExteriorRing();
  		
  		// Make an json path out of this linestring
  		String aRing = linestringToJsonPath(outer);
  		
  		return aRing;
  	}
  	private String assemblePolygon(String aRingOrSeveralRings, Geometry geometry){
  		
  		/* Polygons are like this:
  		 * 		(a) {"rings":[
  		 * 		(b) a ring or several rings (comma separated)
  		 * 		(c) ],
  		 * 		(d) "spatialReference":{" wkid":4326 }
  		 * 		(e) }
  		 */
  		
  		// (1) make spatial reference
  		String spatialReference;
  		try {
  			spatialReference = crsToJsonSpatialReference(geometry);
  		} catch (IOException e) {
  			spatialReference = "\"spatialReference\":{\"wkid\":0000}";
  		}
  		
  		String jsonString = "{\"rings\": [" + aRingOrSeveralRings + "], " + spatialReference + "}";
  		return jsonString;
  	}
  	
  	// General helpers
  	private String crsToJsonSpatialReference(Geometry geom) throws IOException{
  		
  		// Get CRS from geometry
  		String wkid = "";
  		int refsys = geom.getSRID();
  		if (refsys != 0){
  			Integer i = new Integer(refsys);
  			wkid = i.toString();
  		} else {
  			throw new IOException("The GML geometry that is being transformed to a json polyline does not have a spatial reference (srid)");
  		}
  		wkid = "\"spatialReference\":{\"wkid\":" + wkid + "}";
  		return wkid;
  	}
  	
  
  }
  