aboutsummaryrefslogtreecommitdiff
path: root/src/init.cpp
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2018-12-19 13:00:37 +0100
committerEven Rouault <even.rouault@spatialys.com>2018-12-26 10:08:55 +0100
commitdf574ae332d57f556fd56314883b3354cab1d0ff (patch)
tree63a68d40d7ed7932d6329d9c7baa340bb6294f7f /src/init.cpp
parente6de172371ea203f6393d745641d66c82b5b13e2 (diff)
downloadPROJ-df574ae332d57f556fd56314883b3354cab1d0ff.tar.gz
PROJ-df574ae332d57f556fd56314883b3354cab1d0ff.zip
cpp conversion: remove useless pj_, PJ_ and proj_ filename prefixes
Diffstat (limited to 'src/init.cpp')
-rw-r--r--src/init.cpp888
1 files changed, 888 insertions, 0 deletions
diff --git a/src/init.cpp b/src/init.cpp
new file mode 100644
index 00000000..5710031c
--- /dev/null
+++ b/src/init.cpp
@@ -0,0 +1,888 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Initialize projection object from string definition. Includes
+ * pj_init(), pj_init_plus() and pj_free() function.
+ * Author: Gerald Evenden, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
+ *
+ * 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.
+ *****************************************************************************/
+
+#define PJ_LIB__
+
+#include <ctype.h>
+#include <errno.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "geodesic.h"
+#include "proj.h"
+#include "proj_internal.h"
+#include "proj_math.h"
+#include "projects.h"
+
+
+/**************************************************************************************/
+static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) {
+/***************************************************************************************
+ Convert a string (presumably originating from get_init_string) to a paralist.
+***************************************************************************************/
+ char *c = definition;
+ paralist *first = nullptr, *next = nullptr;
+
+ while (*c) {
+ /* Find start of next substring */
+ while (isspace (*c))
+ c++;
+
+ /* Keep a handle to the start of the list, so we have something to return */
+ if (nullptr==first)
+ first = next = pj_mkparam_ws (c);
+ else
+ next = next->next = pj_mkparam_ws (c);
+ if (nullptr==next) {
+ pj_dealloc_params (ctx, first, ENOMEM);
+ return nullptr;
+ }
+
+ /* And skip to the end of the substring */
+ while ((!isspace(*c)) && 0!=*c)
+ c++;
+ }
+
+ if( next == nullptr )
+ return nullptr;
+
+ /* Terminate list and return */
+ next->next = nullptr;
+ return first;
+}
+
+
+
+/**************************************************************************************/
+static char *get_init_string (PJ_CONTEXT *ctx, const char *name) {
+/***************************************************************************************
+ Read a section of an init file. Return its contents as a plain character string.
+ It is the duty of the caller to free the memory allocated for the string.
+***************************************************************************************/
+#define MAX_LINE_LENGTH 1000
+ size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1);
+ char *fname, *section;
+ const char *key;
+ char *buffer = nullptr;
+ char *line = nullptr;
+ PAFile fid;
+ size_t n;
+
+
+ line = static_cast<char*>(pj_malloc (MAX_LINE_LENGTH + 1));
+ if (nullptr==line)
+ return nullptr;
+
+ fname = static_cast<char*>(pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3));
+ if (nullptr==fname) {
+ pj_dealloc (line);
+ return nullptr;
+ }
+
+ /* Support "init=file:section", "+init=file:section", and "file:section" format */
+ key = strstr (name, "init=");
+ if (nullptr==key)
+ key = name;
+ else
+ key += 5;
+ if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return nullptr;
+ }
+ memmove (fname, key, strlen (key) + 1);
+
+ /* Locate the name of the section we search for */
+ section = strrchr(fname, ':');
+ if (nullptr==section) {
+ proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ return nullptr;
+ }
+ *section = 0;
+ section++;
+ n = strlen (section);
+ pj_log (ctx, PJ_LOG_TRACE,
+ "get_init_string: searching for section [%s] in init file [%s]",
+ section, fname);
+
+ fid = pj_open_lib (ctx, fname, "rt");
+ if (nullptr==fid) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
+ return nullptr;
+ }
+
+ /* Search for section in init file */
+ for (;;) {
+
+ /* End of file? */
+ if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) {
+ pj_dealloc (buffer);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE);
+ return nullptr;
+ }
+
+ /* At start of right section? */
+ pj_chomp (line);
+ if ('<'!=line[0])
+ continue;
+ if (strlen (line) < n + 2)
+ continue;
+ if (line[n + 1] != '>')
+ continue;
+ if (0==strncmp (line + 1, section, n))
+ break;
+ }
+
+ /* We're at the first line of the right section - copy line to buffer */
+ buffer = static_cast<char*>(pj_malloc (current_buffer_size));
+ if (nullptr==buffer) {
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ pj_ctx_fclose (ctx, fid);
+ return nullptr;
+ }
+
+ /* Skip the "<section>" indicator, and copy the rest of the line over */
+ strcpy (buffer, line + strlen (section) + 2);
+
+ /* Copy the remaining lines of the section to buffer */
+ for (;;) {
+ char *end_i_cator;
+ size_t next_length, buffer_length;
+
+ /* Did the section end somewhere in the most recently read line? */
+ end_i_cator = strchr (buffer, '<');
+ if (end_i_cator) {
+ *end_i_cator = 0;
+ break;
+ }
+
+ /* End of file? - done! */
+ if (nullptr==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid))
+ break;
+
+ /* Otherwise, handle the line. It MAY be the start of the next section, */
+ /* but that will be handled at the start of next trip through the loop */
+ buffer_length = strlen (buffer);
+ pj_chomp (line); /* Remove '#' style comments */
+ next_length = strlen (line) + buffer_length + 2;
+ if (next_length > current_buffer_size) {
+ char *b = static_cast<char*>(pj_malloc (2 * current_buffer_size));
+ if (nullptr==b) {
+ pj_dealloc (buffer);
+ buffer = nullptr;
+ break;
+ }
+ strcpy (b, buffer);
+ current_buffer_size *= 2;
+ pj_dealloc (buffer);
+ buffer = b;
+ }
+ buffer[buffer_length] = ' ';
+ strcpy (buffer + buffer_length + 1, line);
+ }
+
+ pj_ctx_fclose (ctx, fid);
+ pj_dealloc (fname);
+ pj_dealloc (line);
+ if (nullptr==buffer)
+ return nullptr;
+ pj_shrink (buffer);
+ pj_log (ctx, PJ_LOG_TRACE, "key=%s, value: [%s]", key, buffer);
+ return buffer;
+}
+
+
+
+/************************************************************************/
+static paralist *get_init(PJ_CONTEXT *ctx, const char *key, int allow_init_epsg) {
+/*************************************************************************
+Expand key from buffer or (if not in buffer) from init file
+*************************************************************************/
+ const char *xkey;
+ char *definition = nullptr;
+ paralist *init_items = nullptr;
+
+ /* support "init=file:section", "+init=file:section", and "file:section" format */
+ xkey = strstr (key, "init=");
+ if (nullptr==xkey)
+ xkey = key;
+ else
+ xkey += 5;
+ pj_log (ctx, PJ_LOG_TRACE, "get_init: searching cache for key: [%s]", xkey);
+
+ /* Is file/key pair already in cache? */
+ init_items = pj_search_initcache (xkey);
+ if (init_items)
+ return init_items;
+
+ if( (strncmp(xkey, "epsg:", 5) == 0 || strncmp(xkey, "IGNF:", 5) == 0) ) {
+ char unused[256];
+ char initname[5];
+ int exists;
+
+ memcpy(initname, xkey, 4);
+ initname[4] = 0;
+
+ if( strncmp(xkey, "epsg:", 5) == 0 ) {
+ exists = ctx->epsg_file_exists;
+ if( exists < 0 ) {
+ exists = pj_find_file(ctx, initname, unused, sizeof(unused));
+ ctx->epsg_file_exists = exists;
+ }
+ } else {
+ exists = pj_find_file(ctx, initname, unused, sizeof(unused));
+ }
+
+ if( !exists ) {
+ const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", nullptr };
+ char szInitStr[7 + 64];
+ PJ_OBJ* src;
+ const char* proj_string;
+
+ pj_ctx_set_errno( ctx, 0 );
+
+ if( !allow_init_epsg ) {
+ pj_log (ctx, PJ_LOG_TRACE, "%s expansion disallowed", xkey);
+ return nullptr;
+ }
+ if( strlen(xkey) > 64 ) {
+ return nullptr;
+ }
+ strcpy(szInitStr, "+init=");
+ strcat(szInitStr, xkey);
+
+ src = proj_obj_create_from_user_input(ctx, szInitStr, optionsProj4Mode);
+ if( !src ) {
+ return nullptr;
+ }
+
+ proj_string = proj_obj_as_proj_string(ctx, src, PJ_PROJ_4, nullptr);
+ if( !proj_string ) {
+ proj_obj_destroy(src);
+ return nullptr;
+ }
+ definition = (char*)calloc(1, strlen(proj_string)+1);
+ if( definition ) {
+ strcpy(definition, proj_string);
+ }
+
+ proj_obj_destroy(src);
+ }
+ }
+
+ if( !definition ) {
+ /* If not, we must read it from file */
+ pj_log (ctx, PJ_LOG_TRACE,
+ "get_init: searching on in init files for [%s]", xkey);
+ definition = get_init_string (ctx, xkey);
+ }
+
+ if (nullptr==definition)
+ return nullptr;
+ init_items = string_to_paralist (ctx, definition);
+ if (init_items)
+ pj_log (ctx, PJ_LOG_TRACE, "get_init: got [%s], paralist[0,1]: [%s,%s]",
+ definition,
+ init_items->param,
+ init_items->next ? init_items->next->param : "(empty)");
+ pj_dealloc (definition);
+ if (nullptr==init_items)
+ return nullptr;
+
+ /* We found it in file - now insert into the cache, before returning */
+ pj_insert_initcache (xkey, init_items);
+ return init_items;
+}
+
+
+
+static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, const char *key, int allow_init_epsg) {
+ paralist *defaults, *last = nullptr;
+ char keystring[ID_TAG_MAX + 20];
+ paralist *next, *proj;
+ int err;
+
+ if (nullptr==start)
+ return nullptr;
+
+ if (strlen(key) > ID_TAG_MAX)
+ return nullptr;
+
+ /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */
+ /* or implicitly, because we are initializing a pipeline) */
+ if (pj_param_exists (start, "no_defs"))
+ return start;
+ proj = pj_param_exists (start, "proj");
+ if (nullptr==proj)
+ return start;
+ if (strlen (proj->param) < 6)
+ return start;
+ if (0==strcmp ("pipeline", proj->param + 5))
+ return start;
+
+ err = pj_ctx_get_errno (ctx);
+ pj_ctx_set_errno (ctx, 0);
+
+ /* Locate end of start-list */
+ for (last = start; last->next; last = last->next);
+
+ strcpy (keystring, "proj_def.dat:");
+ strcat (keystring, key);
+ defaults = get_init (ctx, keystring, allow_init_epsg);
+
+ /* Defaults are optional - so we don't care if we cannot open the file */
+ pj_ctx_set_errno (ctx, err);
+
+ if (!defaults)
+ return last;
+
+ /* Loop over all default items */
+ for (next = defaults; next; next = next->next) {
+
+ /* Don't override existing parameter value of same name */
+ if (pj_param_exists (start, next->param))
+ continue;
+
+ /* Don't default ellipse if datum, ellps or any ellipsoid information is set */
+ if (0==strncmp(next->param,"ellps=", 6)) {
+ if (pj_param_exists (start, "datum")) continue;
+ if (pj_param_exists (start, "ellps")) continue;
+ if (pj_param_exists (start, "a")) continue;
+ if (pj_param_exists (start, "b")) continue;
+ if (pj_param_exists (start, "rf")) continue;
+ if (pj_param_exists (start, "f")) continue;
+ if (pj_param_exists (start, "e")) continue;
+ if (pj_param_exists (start, "es")) continue;
+ }
+
+ /* If we're here, it's OK to append the current default item */
+ last = last->next = pj_mkparam(next->param);
+ }
+ last->next = nullptr;
+
+ pj_dealloc_params (ctx, defaults, 0);
+ return last;
+}
+
+/*****************************************************************************/
+static paralist *pj_expand_init_internal(PJ_CONTEXT *ctx, paralist *init, int allow_init_epsg) {
+/******************************************************************************
+Append expansion of <key> to the paralist <init>. The expansion is appended,
+rather than inserted at <init>'s place, since <init> may contain
+overrides to the expansion. These must take precedence, and hence come first
+in the expanded list.
+
+Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm
+zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid.
+
+The expression 'init=foo:bar ellps=intl' will then expand to:
+
+ 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80',
+
+where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence,
+turning the expansion into an UTM projection on the Hayford ellipsoid.
+
+Note that 'init=foo:bar' stays in the list. It is ignored after expansion.
+
+******************************************************************************/
+ paralist *last;
+ paralist *expn;
+
+ /* Nowhere to start? */
+ if (nullptr==init)
+ return nullptr;
+
+ expn = get_init(ctx, init->param, allow_init_epsg);
+
+ /* Nothing in expansion? */
+ if (nullptr==expn)
+ return nullptr;
+
+ /* Locate the end of the list */
+ for (last = init; last && last->next; last = last->next);
+
+ /* Then append and return */
+ last->next = expn;
+ return init;
+}
+
+paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) {
+ return pj_expand_init_internal(ctx, init, TRUE);
+}
+
+
+/************************************************************************/
+/* pj_init_plus() */
+/* */
+/* Same as pj_init() except it takes one argument string with */
+/* individual arguments preceded by '+', such as "+proj=utm */
+/* +zone=11 +ellps=WGS84". */
+/************************************************************************/
+
+PJ *
+pj_init_plus( const char *definition )
+
+{
+ return pj_init_plus_ctx( pj_get_default_ctx(), definition );
+}
+
+PJ *
+pj_init_plus_ctx( projCtx ctx, const char *definition )
+{
+#define MAX_ARG 200
+ char *argv[MAX_ARG];
+ char *defn_copy;
+ int argc = 0, i, blank_count = 0;
+ PJ *result = nullptr;
+
+ /* make a copy that we can manipulate */
+ defn_copy = (char *) pj_malloc( strlen(definition)+1 );
+ if (!defn_copy)
+ return nullptr;
+ strcpy( defn_copy, definition );
+
+ /* split into arguments based on '+' and trim white space */
+
+ for( i = 0; defn_copy[i] != '\0'; i++ )
+ {
+ switch( defn_copy[i] )
+ {
+ case '+':
+ if( i == 0 || defn_copy[i-1] == '\0' || blank_count > 0 )
+ {
+ /* trim trailing spaces from the previous param */
+ if( blank_count > 0 )
+ {
+ defn_copy[i - blank_count] = '\0';
+ blank_count = 0;
+ }
+
+ if( argc+1 == MAX_ARG )
+ {
+ pj_dalloc( defn_copy );
+ pj_ctx_set_errno( ctx, PJD_ERR_UNPARSEABLE_CS_DEF );
+ return nullptr;
+ }
+
+ argv[argc++] = defn_copy + i + 1;
+ }
+ break;
+
+ case ' ':
+ case '\t':
+ case '\n':
+ /* trim leading spaces from the current param */
+ if( i == 0 || defn_copy[i-1] == '\0' || argc == 0 || argv[argc-1] == defn_copy + i )
+ defn_copy[i] = '\0';
+ else
+ blank_count++;
+ break;
+
+ default:
+ /* reset blank_count */
+ blank_count = 0;
+ }
+ }
+ /* trim trailing spaces from the last param */
+ defn_copy[i - blank_count] = '\0';
+
+ /* perform actual initialization */
+ result = pj_init_ctx( ctx, argc, argv );
+
+ pj_dalloc( defn_copy );
+ return result;
+}
+
+
+
+/************************************************************************/
+/* pj_init() */
+/* */
+/* Main entry point for initialing a PJ projections */
+/* definition. Note that the projection specific function is */
+/* called to do the initial allocation so it can be created */
+/* large enough to hold projection specific parameters. */
+/************************************************************************/
+
+PJ *
+pj_init(int argc, char **argv) {
+ return pj_init_ctx( pj_get_default_ctx(), argc, argv );
+}
+
+
+static PJ_CONSTRUCTOR locate_constructor (const char *name) {
+ int i;
+ const char *s;
+ const PJ_OPERATIONS *operations;
+ operations = proj_list_operations();
+ for (i = 0; (s = operations[i].id) && strcmp(name, s) ; ++i) ;
+ if (nullptr==s)
+ return nullptr;
+ return (PJ_CONSTRUCTOR) operations[i].proj;
+}
+
+
+PJ *
+pj_init_ctx(projCtx ctx, int argc, char **argv) {
+ /* Legacy interface: allow init=epsg:XXXX syntax by default */
+ int allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, TRUE);
+ return pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg);
+}
+
+
+PJ *
+pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_init_epsg) {
+ const char *s;
+ char *name;
+ PJ_CONSTRUCTOR proj;
+ paralist *curr, *init, *start;
+ int i;
+ int err;
+ PJ *PIN = nullptr;
+ int n_pipelines = 0;
+ int n_inits = 0;
+ const PJ_UNITS *units;
+ const PJ_PRIME_MERIDIANS *prime_meridians;
+
+ if (nullptr==ctx)
+ ctx = pj_get_default_ctx ();
+
+ ctx->last_errno = 0;
+
+ if (argc <= 0) {
+ pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS);
+ return nullptr;
+ }
+
+ /* count occurrences of pipelines and inits */
+ for (i = 0; i < argc; ++i) {
+ if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") )
+ n_pipelines++;
+ if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5))
+ n_inits++;
+ }
+
+ /* can't have nested pipelines directly */
+ if (n_pipelines > 1) {
+ pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE);
+ return nullptr;
+ }
+
+ /* don't allow more than one +init in non-pipeline operations */
+ if (n_pipelines == 0 && n_inits > 1) {
+ pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS);
+ return nullptr;
+ }
+
+
+ /* put arguments into internal linked list */
+ start = curr = pj_mkparam(argv[0]);
+ if (!curr) {
+ pj_dealloc_params (ctx, start, ENOMEM);
+ return nullptr;
+ }
+
+ for (i = 1; i < argc; ++i) {
+ curr->next = pj_mkparam(argv[i]);
+ if (!curr->next) {
+ pj_dealloc_params (ctx, start, ENOMEM);
+ return nullptr;
+ }
+ curr = curr->next;
+ }
+
+
+ /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */
+ /* expanded in the individual pipeline steps during pipeline initialization. */
+ /* Potentially this leads to many nested pipelines, which shouldn't be a */
+ /* problem when '+init's are expanded as late as possible. */
+ init = pj_param_exists (start, "init");
+ if (init && n_pipelines == 0) {
+ init = pj_expand_init_internal (ctx, init, allow_init_epsg);
+ if (!init) {
+ pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS);
+ return nullptr;
+ }
+ }
+ if (ctx->last_errno) {
+ pj_dealloc_params (ctx, start, ctx->last_errno);
+ return nullptr;
+ }
+
+ /* Find projection selection */
+ curr = pj_param_exists (start, "proj");
+ if (nullptr==curr) {
+ pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
+ return nullptr;
+ }
+ name = curr->param;
+ if (strlen (name) < 6) {
+ pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED);
+ return nullptr;
+ }
+ name += 5;
+
+ proj = locate_constructor (name);
+ if (nullptr==proj) {
+ pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID);
+ return nullptr;
+ }
+
+
+ /* Append general and projection specific defaults to the definition list */
+ append_defaults_to_paralist (ctx, start, "general", allow_init_epsg);
+ append_defaults_to_paralist (ctx, start, name, allow_init_epsg);
+
+
+ /* Allocate projection structure */
+ PIN = proj(nullptr);
+ if (nullptr==PIN) {
+ pj_dealloc_params (ctx, start, ENOMEM);
+ return nullptr;
+ }
+
+
+ PIN->ctx = ctx;
+ PIN->params = start;
+ PIN->is_latlong = 0;
+ PIN->is_geocent = 0;
+ PIN->is_long_wrap_set = 0;
+ PIN->long_wrap_center = 0.0;
+ strcpy( PIN->axis, "enu" );
+
+ PIN->gridlist = nullptr;
+ PIN->gridlist_count = 0;
+
+ PIN->vgridlist_geoid = nullptr;
+ PIN->vgridlist_geoid_count = 0;
+
+ /* Set datum parameters. Similarly to +init parameters we want to expand */
+ /* +datum parameters as late as possible when dealing with pipelines. */
+ /* otherwise only the first occurrence of +datum will be expanded and that */
+ if (n_pipelines == 0) {
+ if (pj_datum_set(ctx, start, PIN))
+ return pj_default_destructor (PIN, proj_errno(PIN));
+ }
+
+ err = pj_ellipsoid (PIN);
+
+ if (err) {
+ /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */
+ if (PIN->need_ellps) {
+ pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere");
+ return pj_default_destructor (PIN, proj_errno(PIN));
+ }
+ else {
+ if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN))
+ proj_errno_reset (PIN);
+ PIN->f = 1.0/298.257223563;
+ PIN->a_orig = PIN->a = 6378137.0;
+ PIN->es_orig = PIN->es = PIN->f*(2-PIN->f);
+ }
+ }
+ PIN->a_orig = PIN->a;
+ PIN->es_orig = PIN->es;
+ if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es))
+ return pj_default_destructor (PIN, PJD_ERR_ECCENTRICITY_IS_ONE);
+
+ /* Now that we have ellipse information check for WGS84 datum */
+ if( PIN->datum_type == PJD_3PARAM
+ && PIN->datum_params[0] == 0.0
+ && PIN->datum_params[1] == 0.0
+ && PIN->datum_params[2] == 0.0
+ && PIN->a == 6378137.0
+ && ABS(PIN->es - 0.006694379990) < 0.000000000050 )/*WGS84/GRS80*/
+ {
+ PIN->datum_type = PJD_WGS84;
+ }
+
+ /* Set PIN->geoc coordinate system */
+ PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i);
+
+ /* Over-ranging flag */
+ PIN->over = pj_param(ctx, start, "bover").i;
+
+ /* Vertical datum geoid grids */
+ PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i;
+ if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */
+ pj_param(ctx, start, "sgeoidgrids");
+
+ /* Longitude center for wrapping */
+ PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i;
+ if (PIN->is_long_wrap_set) {
+ PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f;
+ /* Don't accept excessive values otherwise we might perform badly */
+ /* when correcting longitudes around it */
+ /* The test is written this way to error on long_wrap_center "=" NaN */
+ if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) )
+ return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT);
+ }
+
+ /* Axis orientation */
+ if( (pj_param(ctx, start,"saxis").s) != nullptr )
+ {
+ const char *axis_legal = "ewnsud";
+ const char *axis_arg = pj_param(ctx, start,"saxis").s;
+ if( strlen(axis_arg) != 3 )
+ return pj_default_destructor (PIN, PJD_ERR_AXIS);
+
+ if( strchr( axis_legal, axis_arg[0] ) == nullptr
+ || strchr( axis_legal, axis_arg[1] ) == nullptr
+ || strchr( axis_legal, axis_arg[2] ) == nullptr)
+ return pj_default_destructor (PIN, PJD_ERR_AXIS);
+
+ /* TODO: it would be nice to validate we don't have on axis repeated */
+ strcpy( PIN->axis, axis_arg );
+ }
+
+ /* Central meridian */
+ PIN->lam0=pj_param(ctx, start, "rlon_0").f;
+
+ /* Central latitude */
+ PIN->phi0 = pj_param(ctx, start, "rlat_0").f;
+
+ /* False easting and northing */
+ PIN->x0 = pj_param(ctx, start, "dx_0").f;
+ PIN->y0 = pj_param(ctx, start, "dy_0").f;
+ PIN->z0 = pj_param(ctx, start, "dz_0").f;
+ PIN->t0 = pj_param(ctx, start, "dt_0").f;
+
+ /* General scaling factor */
+ if (pj_param(ctx, start, "tk_0").i)
+ PIN->k0 = pj_param(ctx, start, "dk_0").f;
+ else if (pj_param(ctx, start, "tk").i)
+ PIN->k0 = pj_param(ctx, start, "dk").f;
+ else
+ PIN->k0 = 1.;
+ if (PIN->k0 <= 0.)
+ return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO);
+
+ /* Set units */
+ units = proj_list_units();
+ s = nullptr;
+ if ((name = pj_param(ctx, start, "sunits").s) != nullptr) {
+ for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ;
+ if (!s)
+ return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID);
+ s = units[i].to_meter;
+ }
+ if (s || (s = pj_param(ctx, start, "sto_meter").s)) {
+ double factor;
+ int ratio = 0;
+
+ /* ratio number? */
+ if (strlen (s) > 1 && s[0] == '1' && s[1]=='/') {
+ ratio = 1;
+ s += 2;
+ }
+
+ factor = pj_strtod(s, nullptr);
+ if ((factor <= 0.0) || (1/factor==0))
+ return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0);
+
+ PIN->to_meter = ratio? 1 / factor: factor;
+ PIN->fr_meter = 1 / PIN->to_meter;
+
+ } else
+ PIN->to_meter = PIN->fr_meter = 1.;
+
+ /* Set vertical units */
+ s = nullptr;
+ if ((name = pj_param(ctx, start, "svunits").s) != nullptr) {
+ for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ;
+ if (!s)
+ return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID);
+ s = units[i].to_meter;
+ }
+ if (s || (s = pj_param(ctx, start, "svto_meter").s)) {
+ PIN->vto_meter = pj_strtod(s, nullptr);
+ if (*s == '/') /* ratio number */
+ PIN->vto_meter /= pj_strtod(++s, nullptr);
+ if (PIN->vto_meter <= 0.0)
+ return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0);
+ PIN->vfr_meter = 1. / PIN->vto_meter;
+ } else {
+ PIN->vto_meter = PIN->to_meter;
+ PIN->vfr_meter = PIN->fr_meter;
+ }
+
+ /* Prime meridian */
+ prime_meridians = proj_list_prime_meridians();
+ s = nullptr;
+ if ((name = pj_param(ctx, start, "spm").s) != nullptr) {
+ const char *value = nullptr;
+ char *next_str = nullptr;
+
+ for (i = 0; prime_meridians[i].id != nullptr; ++i )
+ {
+ if( strcmp(name,prime_meridians[i].id) == 0 )
+ {
+ value = prime_meridians[i].defn;
+ break;
+ }
+ }
+
+ if( value == nullptr
+ && (dmstor_ctx(ctx,name,&next_str) != 0.0 || *name == '0')
+ && *next_str == '\0' )
+ value = name;
+
+ if (!value)
+ return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN);
+ PIN->from_greenwich = dmstor_ctx(ctx,value,nullptr);
+ }
+ else
+ PIN->from_greenwich = 0.0;
+
+ /* Private object for the geodesic functions */
+ PIN->geod = static_cast<struct geod_geodesic*>(pj_calloc (1, sizeof (struct geod_geodesic)));
+ if (nullptr==PIN->geod)
+ return pj_default_destructor (PIN, ENOMEM);
+ geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es)));
+
+ /* Projection specific initialization */
+ err = proj_errno_reset (PIN);
+ PIN = proj(PIN);
+ if (proj_errno (PIN)) {
+ pj_free(PIN);
+ return nullptr;
+ }
+ proj_errno_restore (PIN, err);
+ return PIN;
+}