aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jniwrap/org/proj4/PJ.java290
-rw-r--r--jniwrap/org/proj4/PJException.java58
-rw-r--r--jniwrap/org/proj4/package-info.java38
-rw-r--r--src/jniproj.c441
-rw-r--r--src/org_proj4_PJ.h135
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 (&epsilon;&sup2;). The eccentricity
+ * is related to axis length by &epsilon;=√(1-(<var>b</var>/<var>a</var>)&sup2;). 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>,&hellip;) 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>,&lt;<var>z</var>&gt;,&hellip;) 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