diff options
| -rw-r--r-- | jniwrap/org/proj4/PJ.java | 290 | ||||
| -rw-r--r-- | jniwrap/org/proj4/PJException.java | 58 | ||||
| -rw-r--r-- | jniwrap/org/proj4/package-info.java | 38 | ||||
| -rw-r--r-- | src/jniproj.c | 441 | ||||
| -rw-r--r-- | src/org_proj4_PJ.h | 135 |
5 files changed, 950 insertions, 12 deletions
diff --git a/jniwrap/org/proj4/PJ.java b/jniwrap/org/proj4/PJ.java new file mode 100644 index 00000000..21bf7a5a --- /dev/null +++ b/jniwrap/org/proj4/PJ.java @@ -0,0 +1,290 @@ +/****************************************************************************** + * $Id$ + * + * Project: PROJ.4 + * Purpose: Java/JNI wrappers for PROJ.4 API. + * Author: Martin Desruisseaux + * + ****************************************************************************** + * Copyright (c) 2011, Open Geospatial Consortium, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ +package org.proj4; + + +/** + * Wraps the <a href="http://proj.osgeo.org/">Proj4</a> {@code PJ} native data structure. + * Almost every methods defined in this class are native methods delegating the work to the + * Proj.4 library. This class is the only place where such native methods are defined. + * <p> + * In the Proj.4 library, the {@code PJ} structure aggregates in a single place information usually + * splitted in many different ISO 19111 interfaces: {@link org.opengis.referencing.datum.Ellipsoid}, + * {@link org.opengis.referencing.datum.Datum}, {@link org.opengis.referencing.datum.PrimeMeridian}, + * {@link org.opengis.referencing.cs.CoordinateSystem}, {@link org.opengis.referencing.crs.CoordinateReferenceSystem} + * and their sub-interfaces. The relationship with the GeoAPI methods is indicated in the + * "See" tags when appropriate. + * + * @author Martin Desruisseaux (Geomatys) + */ +public class PJ { + /** + * The maximal number of dimension accepted by the {@link #transform(PJ, int, double[], int, int)} + * method. This upper limit is actually somewhat arbitrary. This limit exists mostly as a safety + * against potential misuse. + */ + public static final int DIMENSION_MAX = 100; + // IMPLEMENTATION NOTE: if the value is modified, edit also the native C file. + + /** + * Loads the Proj4 library. + */ + static { + System.loadLibrary("proj"); + } + + /** + * The pointer to {@code PJ} structure allocated in the C/C++ heap. This value has no + * meaning in Java code. <strong>Do not modify</strong>, since this value is used by Proj4. + * Do not rename neither, unless you update accordingly the C code in JNI wrappers. + */ + private final long ptr; + + /** + * Creates a new {@code PJ} structure from the given Proj4 definition string. + * + * @param definition The Proj.4 definition string. + * @throws IllegalArgumentException If the PJ structure can not be created from the given string. + */ + public PJ(final String definition) throws IllegalArgumentException { + ptr = allocatePJ(definition); + if (ptr == 0) { + throw new IllegalArgumentException(definition); + } + } + + /** + * Creates a new {@code PJ} structure derived from an existing {@code PJ} object. + * This constructor is usually for getting the + * {@linkplain org.opengis.referencing.crs.ProjectedCRS#getBaseCRS() base geographic CRS} + * from a {@linkplain org.opengis.referencing.crs.ProjectedCRS projected CRS}. + * + * @param crs The CRS (usually projected) from which to derive a new CRS. + * @param type The type of the new CRS. Currently, only {@link Type#GEOGRAPHIC} is supported. + * @throws IllegalArgumentException If the PJ structure can not be created. + */ + public PJ(final PJ crs, final Type type) throws IllegalArgumentException { + if (crs == null) { + // TODO: Use Objects with JDK 7. + throw new NullPointerException("The CRS must be non-null."); + } + if (type != Type.GEOGRAPHIC) { + throw new IllegalArgumentException("Can not derive the " + type + " type."); + } + ptr = allocateGeoPJ(crs); + if (ptr == 0) { + throw new IllegalArgumentException(crs.getLastError()); + } + } + + /** + * Allocates a PJ native data structure and returns the pointer to it. This method should be + * invoked by the constructor only, and the return value <strong>must</strong> be assigned + * to the {@link #ptr} field. The allocated structure is released by the {@link #finalize()} + * method. + * + * @param definition The Proj4 definition string. + * @return A pointer to the PJ native data structure, or 0 if the operation failed. + */ + private static native long allocatePJ(String definition); + + /** + * Allocates a PJ native data structure for the base geographic CRS of the given CRS, and + * returns the pointer to it. This method should be invoked by the constructor only, and + * the return value <strong>must</strong> be assigned to the {@link #ptr} field. + * The allocated structure is released by the {@link #finalize()} method. + * + * @param projected The CRS from which to derive the base geographic CRS. + * @return A pointer to the PJ native data structure, or 0 if the operation failed. + */ + private static native long allocateGeoPJ(PJ projected); + + /** + * Returns the version number of the Proj4 library. + * + * @return The Proj.4 release string. + */ + public static native String getVersion(); + + /** + * Returns the Proj4 definition string. This is the string given to the constructor, + * expanded with as much information as possible. + * + * @return The Proj4 definition string. + */ + public native String getDefinition(); + + /** + * Returns the Coordinate Reference System type. + * + * @return The CRS type. + */ + public native Type getType(); + + /** + * The coordinate reference system (CRS) type returned by {@link PJ#getType()}. + * In the Proj.4 library, a CRS can only be geographic, geocentric or projected, + * without distinction between 2D and 3D CRS. + * + * @author Martin Desruisseaux (Geomatys) + */ + public static enum Type { + /* + * IMPLEMENTATION NOTE: Do not rename those fields, unless you update the + * native C code accordingly. + */ + + /** + * The CRS is of type {@link org.opengis.referencing.crs.GeographicCRS}. + * The CRS can be two-dimensional or three-dimensional. + */ + GEOGRAPHIC, + + /** + * The CRS is of type {@link org.opengis.referencing.crs.GeocentricCRS}. + * The CRS can only be three-dimensional. + */ + GEOCENTRIC, + + /** + * The CRS is of type {@link org.opengis.referencing.crs.ProjectedCRS}. + * The CRS can be two-dimensional or three-dimensional. + */ + PROJECTED + } + + /** + * Returns the value stored in the {@code a_orig} PJ field. + * + * @return The axis length stored in {@code a_orig}. + * + * @see org.opengis.referencing.datum.Ellipsoid#getSemiMajorAxis() + */ + public native double getSemiMajorAxis(); + + /** + * Returns the value computed from PJ fields by {@code √((a_orig)² × (1 - es_orig))}. + * + * @return The axis length computed by {@code √((a_orig)² × (1 - es_orig))}. + * + * @see org.opengis.referencing.datum.Ellipsoid#getSemiMinorAxis() + */ + public native double getSemiMinorAxis(); + + /** + * Returns the square of the ellipsoid eccentricity (ε²). The eccentricity + * is related to axis length by ε=√(1-(<var>b</var>/<var>a</var>)²). The + * eccentricity of a sphere is zero. + * + * @return The eccentricity. + * + * @see org.opengis.referencing.datum.Ellipsoid#isSphere() + * @see org.opengis.referencing.datum.Ellipsoid#getInverseFlattening() + */ + public native double getEccentricitySquared(); + + /** + * Returns an array of character indicating the direction of each axis. Directions are + * characters like {@code 'e'} for East, {@code 'n'} for North and {@code 'u'} for Up. + * + * @return The axis directions. + * + * @see org.opengis.referencing.cs.CoordinateSystemAxis#getDirection() + */ + public native char[] getAxisDirections(); + + /** + * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. + * + * @return The prime meridian longitude, in degrees. + * + * @see org.opengis.referencing.datum.PrimeMeridian#getGreenwichLongitude() + */ + public native double getGreenwichLongitude(); + + /** + * Returns the conversion factor from the linear units to metres. + * + * @param vertical {@code false} for the conversion factor of horizontal axes, + * or {@code true} for the conversion factor of the vertical axis. + * @return The conversion factor to metres for the given axis. + */ + public native double getLinearUnitToMetre(boolean vertical); + + /** + * Transforms in-place the coordinates in the given array. The coordinates array shall contain + * (<var>x</var>,<var>y</var>,<var>z</var>,…) tuples, where the <var>z</var> and + * following dimensions are optional. Note that any dimension after the <var>z</var> value + * are ignored. + * <p> + * Input and output units: + * <p> + * <ul> + * <li>Angular units (as in longitude and latitudes) are decimal degrees.</li> + * <li>Linear units are usually metres, but this is actually projection-dependent.</li> + * </ul> + * + * @param target The target CRS. + * @param dimension The dimension of each coordinate value. Must be in the [2-{@value #DIMENSION_MAX}] range. + * @param coordinates The coordinates to transform, as a sequence of + * (<var>x</var>,<var>y</var>,<<var>z</var>>,…) tuples. + * @param offset Offset of the first coordinate in the given array. + * @param numPts Number of points to transform. + * @throws NullPointerException If the {@code target} or {@code coordinates} argument is null. + * @throws IndexOutOfBoundsException if the {@code offset} or {@code numPts} arguments are invalid. + * @throws PJException If the operation failed for an other reason (provided by Proj4). + * + * @see org.opengis.referencing.operation.MathTransform#transform(double[], int, double[], int, int) + */ + public native void transform(PJ target, int dimension, double[] coordinates, int offset, int numPts) + throws PJException; + + /** + * Returns a description of the last error that occurred, or {@code null} if none. + * + * @return The last error that occurred, or {@code null}. + */ + public native String getLastError(); + + /** + * Returns the string representation of the PJ structure. + * + * @return The string representation. + */ + @Override + public native String toString(); + + /** + * Deallocates the native PJ data structure. This method can be invoked only by the garbage + * collector, and must be invoked exactly once (no more, no less). + * <strong>NEVER INVOKE THIS METHOD EXPLICITELY, NEVER OVERRIDE</strong>. + */ + @Override + protected final native void finalize(); +} diff --git a/jniwrap/org/proj4/PJException.java b/jniwrap/org/proj4/PJException.java new file mode 100644 index 00000000..68897703 --- /dev/null +++ b/jniwrap/org/proj4/PJException.java @@ -0,0 +1,58 @@ +/****************************************************************************** + * $Id$ + * + * Project: PROJ.4 + * Purpose: Java/JNI wrappers for PROJ.4 API. + * Author: Martin Desruisseaux + * + ****************************************************************************** + * Copyright (c) 2011, Open Geospatial Consortium, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ +package org.proj4; + + +/** + * Exception thrown when a call to {@link PJ#transform(PJ, int, double[], int, int)} failed. + * + * @author Martin Desruisseaux (Geomatys) + */ +public class PJException extends Exception { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -2580747577812829763L; + + /** + * Constructs a new exception with no message. + */ + public PJException() { + super(); + } + + /** + * Constructs a new exception with the given message. + * + * @param message A message that describe the cause for the failure. + */ + public PJException(final String message) { + super(message); + } +} diff --git a/jniwrap/org/proj4/package-info.java b/jniwrap/org/proj4/package-info.java new file mode 100644 index 00000000..17a5767c --- /dev/null +++ b/jniwrap/org/proj4/package-info.java @@ -0,0 +1,38 @@ +/****************************************************************************** + * $Id$ + * + * Project: PROJ.4 + * Purpose: Java/JNI wrappers for PROJ.4 API. + * Author: Martin Desruisseaux + * + ****************************************************************************** + * Copyright (c) 2011, Open Geospatial Consortium, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +/** + * Wrappers for the <a href="http://proj.osgeo.org/">Proj4</a> library. The {@link org.proj4.PJ} + * class contains only native methods delegating their work to the Proj.4 library. + * For higher-level methods making use of those native methods, see for example the + * <a href="http://www.geoapi.org/geoapi-proj4/index.html">GeoAPI bindings for Proj.4</a>. + * + * @author Martin Desruisseaux (Geomatys) + */ +package org.proj4; diff --git a/src/jniproj.c b/src/jniproj.c index 2c5be106..6e15c6d5 100644 --- a/src/jniproj.c +++ b/src/jniproj.c @@ -4,6 +4,7 @@ * Project: PROJ.4 * Purpose: Java/JNI wrappers for PROJ.4 API. * Author: Antonello Andrea + * Martin Desruisseaux * ****************************************************************************** * Copyright (c) 2005, Antonello Andrea @@ -28,31 +29,443 @@ *****************************************************************************/ /*! -* \file jniproj.c -* -* \brief -* functions used by the java/jni wrappers of jproj4 -* -* -* \author Antonello Andrea -* \date Wed Oct 20 23:10:24 CEST 2004 -*/ + * \file jniproj.c + * + * \brief + * Functions used by the Java Native Interface (JNI) wrappers of Proj.4 + * + * + * \author Antonello Andrea + * \date Wed Oct 20 23:10:24 CEST 2004 + * + * \author Martin Desruisseaux + * \date August 2011 + */ #include "proj_config.h" #ifdef JNI_ENABLED +#include <math.h> #include <string.h> #include "projects.h" -#include "org_proj4_Projections.h" +#include "org_proj4_PJ.h" #include <jni.h> -#define arraysize 300 +#define PJ_FIELD_NAME "ptr" +#define PJ_FIELD_TYPE "J" +#define PJ_MAX_DIMENSION 100 +// The PJ_MAX_DIMENSION value appears also in quoted strings. +// Please perform a search-and-replace if this value is changed. PJ_CVSID("$Id$"); + +/*! + * \brief + * Internal method returning the address of the PJ structure wrapped by the given Java object. + * This function looks for a field named "ptr" and of type "long" (Java signature "J") in the + * given object. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The address of the PJ structure, or NULL if the operation fails (for example + * because the "ptr" field was not found). + */ +PJ *getPJ(JNIEnv *env, jobject object) +{ + jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); + return (id) ? (PJ*) (*env)->GetLongField(env, object, id) : NULL; +} + +/*! + * \brief + * Returns the Proj4 release number. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \return The Proj4 release number, or NULL. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion + (JNIEnv *env, jclass class) +{ + const char *desc = pj_get_release(); + return (desc) ? (*env)->NewStringUTF(env, desc) : NULL; +} + +/*! + * \brief + * Allocates a new PJ structure from a definition string. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \param definition - The string definition to be given to Proj4. + * \return The address of the new PJ structure, or 0 in case of failure. + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ + (JNIEnv *env, jclass class, jstring definition) +{ + const char *def_utf = (*env)->GetStringUTFChars(env, definition, NULL); + if (!def_utf) return 0; // OutOfMemoryError already thrown. + PJ *pj = pj_init_plus(def_utf); + (*env)->ReleaseStringUTFChars(env, definition, def_utf); + return (jlong) pj; +} + +/*! + * \brief + * Allocates a new geographic PJ structure from an existing one. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \param projected - The PJ object from which to derive a new one. + * \return The address of the new PJ structure, or 0 in case of failure. + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ + (JNIEnv *env, jclass class, jobject projected) +{ + PJ *pj = getPJ(env, projected); + return (pj) ? (jlong) pj_latlong_from_proj(pj) : 0; +} + +/*! + * \brief + * Returns the definition string. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The definition string. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + char *desc = pj_get_def(pj, 0); + if (desc) { + jstring str = (*env)->NewStringUTF(env, desc); + pj_dalloc(desc); + return str; + } + } + return NULL; +} + +/*! + * \brief + * Returns the description associated to the PJ structure. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The description associated to the PJ structure. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + const char *desc = pj->descr; + if (desc) { + return (*env)->NewStringUTF(env, desc); + } + } + return NULL; +} + +/*! + * \brief + * Returns the CRS type as one of the PJ.Type enum: GEOGRAPHIC, GEOCENTRIC or PROJECTED. + * This function should never return NULL, unless class or fields have been renamed in + * such a way that we can not find anymore the expected enum values. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The CRS type as one of the PJ.Type enum. + */ +JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + const char *type; + if (pj_is_latlong(pj)) { + type = "GEOGRAPHIC"; + } else if (pj_is_geocent(pj)) { + type = "GEOCENTRIC"; + } else { + type = "PROJECTED"; + } + jclass c = (*env)->FindClass(env, "org/proj4/PJ$Type"); + if (c) { + jfieldID id = (*env)->GetStaticFieldID(env, c, type, "Lorg/proj4/PJ$Type;"); + if (id) { + return (*env)->GetStaticObjectField(env, c, id); + } + } + } + return NULL; +} + +/*! + * \brief + * Returns the semi-major axis length. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The semi-major axis length. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return pj ? pj->a_orig : NAN; +} + +/*! + * \brief + * Computes the semi-minor axis length from the semi-major axis length and the eccentricity + * squared. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The semi-minor axis length. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (!pj) return NAN; + double a = pj->a_orig; + return sqrt(a*a * (1.0 - pj->es_orig)); +} + +/*! + * \brief + * Returns the eccentricity squared. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The eccentricity. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return pj ? pj->es_orig : NAN; +} + +/*! + * \brief + * Returns an array of character indicating the direction of each axis. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The axis directions. + */ +JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + int length = strlen(pj->axis); + jcharArray array = (*env)->NewCharArray(env, length); + if (array) { + jchar* axis = (*env)->GetCharArrayElements(env, array, NULL); + if (axis) { + // Don't use memcp because the type may not be the same. + int i; + for (i=0; i<length; i++) { + axis[i] = pj->axis[i]; + } + (*env)->ReleaseCharArrayElements(env, array, axis, 0); + } + return array; + } + } + return NULL; +} + +/*! + * \brief + * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The prime meridian longitude, in degrees. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return (pj) ? (pj->from_greenwich)*(180/M_PI) : NAN; +} + +/*! + * \brief + * Returns the conversion factor from linear units to metres. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \param vertical - JNI_FALSE for horizontal axes, or JNI_TRUE for the vertical axis. + * \return The conversion factor to metres. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre + (JNIEnv *env, jobject object, jboolean vertical) +{ + PJ *pj = getPJ(env, object); + if (pj) { + return (vertical) ? pj->vto_meter : pj->to_meter; + } + return NAN; +} + +/*! + * \brief + * Converts input values from degrees to radians before coordinate operation, or the output + * values from radians to degrees after the coordinate operation. + * + * \param pj - The Proj.4 PJ structure. + * \param data - The coordinate array to transform. + * \param numPts - Number of points to transform. + * \param dimension - Dimension of points in the coordinate array. + * \param factor - The scale factor to apply: M_PI/180 for inputs or 180/M_PI for outputs. + */ +void convertAngularOrdinates(PJ *pj, double* data, jint numPts, int dimension, double factor) { + int dimToSkip; + if (pj_is_latlong(pj)) { + // Convert only the 2 first ordinates and skip all the other dimensions. + dimToSkip = dimension - 2; + } else if (pj_is_geocent(pj)) { + // Convert only the 3 first ordinates and skip all the other dimensions. + dimToSkip = dimension - 3; + } else { + // Not a geographic or geocentric CRS: nothing to convert. + return; + } + double *stop = data + dimension*numPts; + if (dimToSkip > 0) { + while (data != stop) { + (*data++) *= factor; + (*data++) *= factor; + data += dimToSkip; + } + } else { + while (data != stop) { + (*data++) *= factor; + } + } +} + +/*! + * \brief + * Transforms in-place the coordinates in the given array. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \param target - The target CRS. + * \param dimension - The dimension of each coordinate value. Must be equals or greater than 2. + * \param coordinates - The coordinates to transform, as a sequence of (x,y,<z>,...) tuples. + * \param offset - Offset of the first coordinate in the given array. + * \param numPts - Number of points to transform. + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_transform + (JNIEnv *env, jobject object, jobject target, jint dimension, jdoubleArray coordinates, jint offset, jint numPts) +{ + if (!target || !coordinates) { + jclass c = (*env)->FindClass(env, "java/lang/NullPointerException"); + if (c) (*env)->ThrowNew(env, c, "The target CRS and the coordinates array can not be null."); + return; + } + if (dimension < 2 || dimension > PJ_MAX_DIMENSION) { // Arbitrary upper value for catching potential misuse. + jclass c = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); + if (c) (*env)->ThrowNew(env, c, "Illegal dimension. Must be in the [2-100] range."); + return; + } + if ((offset < 0) || (numPts < 0) || (offset + dimension*numPts) > (*env)->GetArrayLength(env, coordinates)) { + jclass c = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException"); + if (c) (*env)->ThrowNew(env, c, "Illegal offset or illegal number of points."); + return; + } + PJ *src_pj = getPJ(env, object); + PJ *dst_pj = getPJ(env, target); + if (src_pj && dst_pj) { + // Using GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical rather than + // GetDoubleArrayElements/ReleaseDoubleArrayElements increase the chances that + // the JVM returns direct reference to its internal array without copying data. + // However we must promise to run the "critical" code fast, to not make any + // system call that may wait for the JVM and to not invoke any other JNI method. + double *data = (*env)->GetPrimitiveArrayCritical(env, coordinates, NULL); + if (data) { + double *x = data + offset; + double *y = x + 1; + double *z = (dimension >= 3) ? y+1 : NULL; + convertAngularOrdinates(src_pj, x, numPts, dimension, M_PI/180); + int err = pj_transform(src_pj, dst_pj, numPts, dimension, x, y, z); + convertAngularOrdinates(dst_pj, x, numPts, dimension, 180/M_PI); + (*env)->ReleasePrimitiveArrayCritical(env, coordinates, data, 0); + if (err) { + jclass c = (*env)->FindClass(env, "org/proj4/PJException"); + if (c) (*env)->ThrowNew(env, c, pj_strerrno(err)); + } + } + } +} + +/*! + * \brief + * Returns a description of the last error that occurred, or NULL if none. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The last error, or NULL. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + int err = pj_ctx_get_errno(pj->ctx); + if (err) { + return (*env)->NewStringUTF(env, pj_strerrno(err)); + } + } + return NULL; +} + +/*! + * \brief + * Deallocate the PJ structure. This method is invoked by the garbage collector exactly once. + * This method will also set the Java "ptr" final field to 0 as a safety. In theory we are not + * supposed to change the value of a final field. But no Java code should use this field, and + * the PJ object is being garbage collected anyway. We set the field to 0 as a safety in case + * some user invoked the finalize() method explicitely despite our warning in the Javadoc to + * never do such thing. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize + (JNIEnv *env, jobject object) +{ + jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); + if (id) { + PJ *pj = (PJ*) (*env)->GetLongField(env, object, id); + if (pj) { + (*env)->SetLongField(env, object, id, (jlong) 0); + pj_free(pj); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// + /* - * TODO: There is some work that still need to be done in this file: + * Below this point are the previous JNI bindings that existed in the legacy org.proj4.Projections + * class before the new bindings defined above. We keep those binding for now, but may remove them + * in a future version. There is a few issues with the code below: * * 1) Every call to (*env)->GetStringUTFChars(...) shall have a corresponding call to * (*env)->ReleaseStringUTFChars(...). @@ -69,6 +482,10 @@ PJ_CVSID("$Id$"); * throw the appropriate Java exception if an argument is invalid. */ +#include "org_proj4_Projections.h" + +#define arraysize 300 + /*! * \brief * executes reprojection diff --git a/src/org_proj4_PJ.h b/src/org_proj4_PJ.h new file mode 100644 index 00000000..ed0b8d0e --- /dev/null +++ b/src/org_proj4_PJ.h @@ -0,0 +1,135 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include <jni.h> +/* Header for class org_proj4_PJ */ + +#ifndef _Included_org_proj4_PJ +#define _Included_org_proj4_PJ +#ifdef __cplusplus +extern "C" { +#endif +#undef org_proj4_PJ_DIMENSION_MAX +#define org_proj4_PJ_DIMENSION_MAX 100L +/* + * Class: org_proj4_PJ + * Method: allocatePJ + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ + (JNIEnv *, jclass, jstring); + +/* + * Class: org_proj4_PJ + * Method: allocateGeoPJ + * Signature: (Lorg/proj4/PJ;)J + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ + (JNIEnv *, jclass, jobject); + +/* + * Class: org_proj4_PJ + * Method: getVersion + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion + (JNIEnv *, jclass); + +/* + * Class: org_proj4_PJ + * Method: getDefinition + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getType + * Signature: ()Lorg/proj4/PJ/Type; + */ +JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getSemiMajorAxis + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getSemiMinorAxis + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getEccentricity + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getAxisDirections + * Signature: ()[C + */ +JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getGreenwichLongitude + * Signature: ()D + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: getLinearUnitToMetre + * Signature: (Z)D + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre + (JNIEnv *, jobject, jboolean); + +/* + * Class: org_proj4_PJ + * Method: transform + * Signature: (Lorg/proj4/PJ;I[DII)V + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_transform + (JNIEnv *, jobject, jobject, jint, jdoubleArray, jint, jint); + +/* + * Class: org_proj4_PJ + * Method: getLastError + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: toString + * Signature: ()Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString + (JNIEnv *, jobject); + +/* + * Class: org_proj4_PJ + * Method: finalize + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif |
