/*
* This file is part of the GeoLatte project.
*
* GeoLatte 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, either version 3 of the License, or
* (at your option) any later version.
*
* GeoLatte 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with GeoLatte. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2010 - 2011 and Ownership of code is shared by:
* Qmino bvba - Esperantolaan 4 - 3001 Heverlee (http://www.qmino.com)
* Geovise bvba - Generaal Eisenhowerlei 9 - 2140 Antwerpen (http://www.geovise.com)
*/
package org.geolatte.common.dataformats.json.to;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import java.util.Arrays;
/**
* Abstract parentclass for all geojson transfer objects.
*
* Not to be subclassed.
*
* @author Yves Vandewoude
* @author <a href="http://www.qmino.com">Qmino bvba</a>
*/
@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include= JsonTypeInfo.As.PROPERTY, property="type")
@JsonIgnoreProperties({"valid"})
@JsonSubTypes({
@JsonSubTypes.Type(value=PointTo.class, name="Point"),
@JsonSubTypes.Type(value=MultiPointTo.class, name="MultiPoint"),
@JsonSubTypes.Type(value=LineStringTo.class, name="LineString"),
@JsonSubTypes.Type(value=MultiLineStringTo.class, name="MultiLineString"),
@JsonSubTypes.Type(value=PolygonTo.class, name="Polygon"),
@JsonSubTypes.Type(value=MultiPolygonTo.class, name="MultiPolygon"),
@JsonSubTypes.Type(value=GeometryCollectionTo.class, name="GeometryCollection")
})
public abstract class GeoJsonTo {
private CrsTo crs;
private double[] bbox;
GeoJsonTo() {
}
GeoJsonTo(CrsTo crs, double[] bbox) {
this.crs = crs;
this.bbox = bbox;
}
/**
* @return whether the transfer object corresponds with a valid GeoJson entity
*/
public abstract boolean isValid();
/**
* @return the crs which is specified in this to.
*/
public CrsTo getCrs() {
return crs;
}
/**
* @param crs the crs to set for this geometry
*/
public void setCrs(CrsTo crs) {
this.crs = crs;
}
/**
* @return the boundingbox of this geometry. The value of the bbox member must be a 2*n array where n is
* the number of dimensions represented in the contained geometries, with the lowest values for all axes
* followed by the highest values. The axes order of a bbox follows the axes order of geometries. In
* addition,
* the coordinate reference system for the bbox is assumed to match the coordinate reference system of the
* GeoJSON object of which it is a member.
*/
public double[] getBbox() {
return bbox;
}
/**
* Sets the boundingbox value for this geometry
*
* @param bbox the given value to set (it is not checked for validity)
*/
protected void setBbox(double[] bbox) {
this.bbox = bbox;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null) {
return false;
}
// Geometries can only be equals if they are of the same type
if (!o.getClass().equals(this.getClass())) {
return false;
}
GeoJsonTo geoJsonTo = (GeoJsonTo) o;
if (!Arrays.equals(bbox, geoJsonTo.bbox)) {
return false;
}
if (crs != null ? !crs.equals(geoJsonTo.crs) : geoJsonTo.crs != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = crs != null ? crs.hashCode() : 0;
result = 31 * result + (bbox != null ? Arrays.hashCode(bbox) : 0);
return result;
}
/**
* Convenience method to create a named crs to.
*
* @param crsName the name of the crs to use, if null, the default crs for geojson is used.
* @return the transfer object that corresponds with the default Crs. According to the GeoJson spec
* The default CRS is a geographic coordinate reference system, using the WGS84 datum, and with longitude
* and latitude units of decimal degrees.
*/
public static CrsTo createCrsTo(String crsName) {
String nameToUse = crsName == null ? "EPSG:4326" : crsName;
CrsTo result = new CrsTo();
NamedCrsPropertyTo property = new NamedCrsPropertyTo();
property.setName(nameToUse);
result.setProperties(property);
return result;
}
/**
* Creates a boundingbox for a point. In this case, both the lower and higher edges of the bbox are the point
* itself
*
* @param coordinates the coordinates of the point
* @return the boundingbox, a list with doubles. the result is a 2*n array where n is the number of dimensions
* represented in the input, with the lowest values for all axes followed by the highest values.
*/
public static double[] createBoundingBox(double[] coordinates) {
int maxActualCoords = Math.min(coordinates.length,3);// ignore potential M values, so 4 dimension (X Y Z M) becomes X Y Z
double[] result = new double[maxActualCoords * 2];
for (int i = 0; i < maxActualCoords; i++) {
result[i] = coordinates[i];
result[maxActualCoords + i] = coordinates[i];
}
return result;
}
/**
* This method computes the boundingbox of a list of points (such as the coordinates of a multipoint or linestring)
*
* @param input a list of lists that each contain between two and for doubles (x, y and optional z and m coordinates)
* @return a list with doubles. the result is a 2*n array where n is the number of dimensions represented
* in the input (m doesn't count as a dimension), with the lowest values for all axes followed by the highest values.
*/
public static double[] createBoundingBox(double[][] input) {
int maxActualCoords = Math.min(input[0].length, 3);// max 3, ignoring potential m values
double[] result = new double[maxActualCoords * 2];
for (int i = 0; i < maxActualCoords; i++) {
result[i] = Double.MAX_VALUE;
}
for (int i = maxActualCoords; i < result.length; i++) {
result[i] = - Double.MAX_VALUE; // use negative Double.MAX_VALUE, Double.MIN_VALUE is NOT what you'd expect it to be!
}
for (double[] point : input) {
for (int i = 0; i < maxActualCoords; i++) {
result[i] = Math.min(point[i], result[i]);
result[i + maxActualCoords] = Math.max(point[i], result[i + maxActualCoords]);
}
}
return result;
}
/**
* This method computes the boundingbox of a threedimensional list of doubles, which are to be interpreted as
* a list of lists of coordinates (such as the coordinates of a multilinestring or polygon)
*
* @param input a list of lists that each contain two or three doubles (x, y and optional z coordinates)
* @return a list with doubles. the result is a 2*n array where n is the number of dimensions represented
* in the input, with the lowest values for all axes followed by the highest values.
*/
public static double[] createBoundingBox(double[][][] input) {
double[] bbox = createBoundingBox(input[0]);
for (int i = 1; i < input.length; i++) {
double[] current = createBoundingBox(input[i]);
mergeInto(bbox, current);
}
return bbox;
}
/**
* This method computes the boundingbox of a fourdimensional list of doubles, which are to be interpreted as
* a list of items that each represent list of lists of coordinates (such as the coordinates of a multilipolygon,
* which contains lists of polygons, each containing lists of linearrings that contain coordinates)
*
* @param input a list of lists that each contain two or three doubles (x, y and optional z coordinates)
* @return a list with doubles. the result is a 2*n array where n is the number of dimensions represented
* in the input, with the lowest values for all axes followed by the highest values.
*/
public static double[] createBoundingBox(double[][][][] input) {
double[] bbox = createBoundingBox(input[0]);
for (int i = 1; i < input.length; i++) {
double[] current = createBoundingBox(input[i]);
mergeInto(bbox, current);
}
return bbox;
}
/**
* Merges the second boundingbox into the first. Basically, this extends the first boundingbox to also
* encapsulate the second
*
* @param first the first boundingbox
* @param second the second boundingbox
*/
private static void mergeInto(double[] first, double[] second) {
for (int j = 0; j < first.length / 2; j++) {
first[j] = Math.min(first[j], second[j]);
first[j + first.length / 2] = Math.max(first[j + first.length / 2], second[j + first.length / 2]);
}
}
}