/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library 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
* Lesser General Public License for more details.
*/
package org.geotools.data.sfs;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import org.geotools.data.Query;
import org.geotools.factory.Hints;
import org.geotools.referencing.CRS;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.GeometryDescriptor;
import org.opengis.filter.sort.SortOrder;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystem;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.CoordinateSequenceFilter;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
/**
* A Generic Utility class for misc. methods
* @author
*/
class SFSDataStoreUtil {
static final String DEFAULT_ENCODING = "UTF-8";
/**
* Does the flipping of geometry object incase the axis is YX
* We used CoordinateArraySequence to get coordinate Array
* As per its documentation "In this implementation, Coordinates returned
* by #toArray and #getCoordinate are live -- modifications to them are
* actually changing the CoordinateSequence's underlying data."
* @param fnG
*/
public static void flipFeatureYX(Geometry fnG) {
fnG.apply(new CoordinateSequenceFilter() {
public boolean isGeometryChanged() {
return true;
}
public boolean isDone() {
return false;
}
public void filter(CoordinateSequence seq, int i) {
double x = seq.getX(i);
double y = seq.getY(i);
seq.setOrdinate(i, 0, y);
seq.setOrdinate(i, 1, x);
}
});
}
/**
* Bounding Box has the form [ymin,xmin, ymax, xmax] and wre want to transform
* it in [xmin,ymin,xmax,ymax]
*
* @param fnAL
*/
public static void flipYXInsideTheBoundingBox(ArrayList fnAL) {
double ymin = Double.parseDouble(fnAL.get(0).toString());
double xmin = Double.parseDouble(fnAL.get(1).toString());
double ymax = Double.parseDouble(fnAL.get(2).toString());
double xmax = Double.parseDouble(fnAL.get(3).toString());
/*Clear the contents of the arraylist*/
fnAL.clear();
fnAL.add(0, xmin);
fnAL.add(1, ymin);
fnAL.add(2, xmax);
fnAL.add(3, ymax);
}
/**
* This method parses the query and creates a string which can be appended to
* the baseURL and then execute it to get response
* Pulled from GeoRest DataStore
* @param fnQuery
* @return String
*/
public static String encodeQuery(Query fnQuery, SimpleFeatureType targetType) throws UnsupportedEncodingException {
boolean firstChar = false;
// Transform query into URL parameters:
StringBuilder urlBuilder = new StringBuilder();
// Feature number limitation:
if (!fnQuery.isMaxFeaturesUnlimited()) {
urlBuilder.append(getGlueChar(firstChar));
firstChar = false;
urlBuilder.append("limit=");
urlBuilder.append(fnQuery.getMaxFeatures());
}
boolean no_geom = true;
// Should we retrieve all properties? and also check for no_geom
if (!fnQuery.retrieveAllProperties() && fnQuery.getPropertyNames().length > 0) {
urlBuilder.append(getGlueChar(firstChar));
firstChar = false;
urlBuilder.append("attrs=");
for (int i = 0; i < fnQuery.getPropertyNames().length; i++) {
if (targetType.getDescriptor(fnQuery.getPropertyNames()[i]) instanceof GeometryDescriptor) {
no_geom = false;
}
urlBuilder.append(URLEncoder.encode(fnQuery.getPropertyNames()[i], DEFAULT_ENCODING));
if (i < fnQuery.getPropertyNames().length - 1) {
urlBuilder.append(",");
}
}
/*This is to add no_geom*/
if(no_geom) {
urlBuilder.append(getGlueChar(firstChar));
urlBuilder.append("no_geom=");
urlBuilder.append(no_geom);
}
}
// Perhaps use an offset to start counting from:
if (fnQuery.getStartIndex() != null) {
urlBuilder.append(getGlueChar(firstChar));
firstChar = false;
urlBuilder.append("offset=");
urlBuilder.append(fnQuery.getStartIndex());
}
// Is there a certain order required?
if (fnQuery.getSortBy() != null && fnQuery.getSortBy().length > 0) {
urlBuilder.append(getGlueChar(firstChar));
firstChar = false;
urlBuilder.append("order_by=");
urlBuilder.append(URLEncoder.encode(fnQuery.getSortBy()[0].getPropertyName().getPropertyName(), DEFAULT_ENCODING));
urlBuilder.append(getGlueChar(firstChar));
urlBuilder.append("dir=");
SortOrder order = fnQuery.getSortBy()[0].getSortOrder();
urlBuilder.append(URLEncoder.encode(order.name(), DEFAULT_ENCODING));
}
// Is there a filter present?
if (fnQuery.getFilter() != null) {
SFSFilterVisitor visitor = new SFSFilterVisitor(false);
fnQuery.getFilter().accept(visitor, null);
visitor.finish(urlBuilder, !firstChar);
firstChar = false;
}
// handle the query hints
if (fnQuery.getHints() != null
&& fnQuery.getHints().get(Hints.VIRTUAL_TABLE_PARAMETERS) != null) {
Map<String, String> params = (Map<String, String>) fnQuery.getHints().get(
Hints.VIRTUAL_TABLE_PARAMETERS);
urlBuilder.append(getGlueChar(firstChar));
firstChar = false;
urlBuilder.append("hints=");
for (Iterator it = params.entrySet().iterator(); it.hasNext();) {
Map.Entry<String, String> pair = (Map.Entry<String, String>) it.next();
urlBuilder.append(URLEncoder.encode(pair.getKey().toString(), DEFAULT_ENCODING));
urlBuilder.append(":");
urlBuilder.append(URLEncoder.encode(pair.getValue().toString(), DEFAULT_ENCODING));
if (it.hasNext()) {
urlBuilder.append(";");
}
}
}
if(urlBuilder.length() > 1) {
return urlBuilder.substring(1).toString();
} else {
return "";
}
}
/**
* Method used to construct the URL from query
* @param fcFlag
* @return String
*/
private static String getGlueChar(boolean fcFlag) {
if (fcFlag) {
return "";
}
return "&";
}
/**
*
* @param json
* @return
*/
public static String strip(String json) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < json.length(); i++) {
char c = json.charAt(i);
if (c == ' ' || c == '\n') {
continue;
}
if (c == '\'') {
sb.append("\"");
} else {
sb.append(c);
}
}
return sb.toString();
}
/**
* This method returns the class, which is required to build the feature type
* if input string type in not among String, int, double, boolean,
* timestamp and geometry objects then default string is returned
* @param strObj
* @return Class
*/
public static Class getClass(String strObj) {
if (strObj == null) {
return String.class;
} else {
if (strObj.equalsIgnoreCase("String")) {
return String.class;
} else if (strObj.equalsIgnoreCase("int")) {
return Integer.class;
} else if (strObj.equalsIgnoreCase("double")) {
return Double.class;
} else if (strObj.equalsIgnoreCase("boolean")) {
return Boolean.class;
} else if (strObj.equalsIgnoreCase("geometry")) {
return Geometry.class;
} else if (strObj.equalsIgnoreCase("point")) {
return Point.class;
} else if (strObj.equalsIgnoreCase("multipoint")) {
return MultiPoint.class;
} else if (strObj.equalsIgnoreCase("linestring")) {
return LineString.class;
} else if (strObj.equalsIgnoreCase("multilinestring")) {
return MultiLineString.class;
} else if (strObj.equalsIgnoreCase("polygon")) {
return Polygon.class;
} else if (strObj.equalsIgnoreCase("multipolygon")) {
return MultiPolygon.class;
} else if (strObj.equalsIgnoreCase("geometrycollection")) {
return GeometryCollection.class;
} else if (strObj.equalsIgnoreCase("timestamp")) {
return Date.class;
} else if (strObj.equalsIgnoreCase("number")) {
return Double.class;
} else {
return String.class;
}
}
}
/**
* Decodes a CRS ensuring the axis order is X/Y no matter what syntaxhas been used
* @param srsName
* @return
* @throws NoSuchAuthorityCodeException
* @throws FactoryException
*/
public static CoordinateReferenceSystem decodeXY(String srsName) throws NoSuchAuthorityCodeException, FactoryException {
CoordinateReferenceSystem crs = CRS.decode(srsName, true);
if(!isXYOriented(crs)) {
return CRS.decode("EPSG:" + CRS.lookupEpsgCode(crs, false), true);
} else {
return crs;
}
}
/**
* Returns the axis order of the provided {@link CoordinateReferenceSystem} object.
* TODO: this utility has been taken from GML2Utils, move it to CRS so that it can be shared
*/
static boolean isXYOriented(CoordinateReferenceSystem crs) {
CoordinateSystem cs = null;
if (crs instanceof ProjectedCRS) {
ProjectedCRS pcrs = (ProjectedCRS) crs;
cs = pcrs.getBaseCRS().getCoordinateSystem();
} else if (crs instanceof GeographicCRS) {
cs = crs.getCoordinateSystem();
} else {
return true;
}
int dimension = cs.getDimension();
int longitudeDim = -1;
int latitudeDim = -1;
for (int i = 0; i < dimension; i++) {
AxisDirection dir = cs.getAxis(i).getDirection().absolute();
if (dir.equals(AxisDirection.EAST)) {
longitudeDim = i;
}
if (dir.equals(AxisDirection.NORTH)) {
latitudeDim = i;
}
}
if ((longitudeDim >= 0) && (latitudeDim >= 0)) {
if (longitudeDim < latitudeDim) {
return true;
} else {
return false;
}
}
return false;
}
}