Ticket #150: 0001-cavern-Add-cs-file-to-read-the-coordinate-system-fro.patch

File 0001-cavern-Add-cs-file-to-read-the-coordinate-system-fro.patch, 20.1 KB (added by Philip Schuchardt, 18 hours ago)
  • doc/3dformat.htm

    From 70cef2cd4fe5af3700f64413798c5ac826d888c2 Mon Sep 17 00:00:00 2001
    From: Philip Schuchardt <vpicaver@gmail.com>
    Date: Tue, 11 Aug 2026 19:55:12 -0500
    Subject: [PATCH] cavern: Add *cs file to read the coordinate system from a
     file
    
    `*cs file FILENAME` reads the coordinate system description from a
    separate file, so it can be a PROJ string, WKT or PROJJSON.  WKT and
    PROJJSON are made up of double quoted strings written over several
    lines, which is awkward to write in a .svx file, and they can express
    datums a PROJ string can't, such as NAD83(2011), ETRS89 and GDA2020.
    It also means a .prj file such as those which accompany ESRI
    shapefiles can be used directly, so a survey can be georeferenced to
    match GIS data you already have.
    
    The filename is resolved relative to the directory of the file the
    *cs is in, a .prj extension is tried if the name as given isn't found,
    and the lines of the file are joined with a space since the coordinate
    system is stored in the .3d file as a single line.
    
    This addresses the review of the patch on
    https://trac.survex.com/ticket/150: `file` is a new coordinate system
    keyword rather than an `@` prefix on `custom`, so `custom` still always
    means a literal PROJ/WKT string, and there's no UTF-8 BOM skipping -
    PROJ rejects a BOM and so does GDAL's .prj reader, so cavern now does
    too.
    ---
     doc/3dformat.htm   |  9 ++++-
     doc/datafile.rst   | 29 +++++++++++++++
     src/commands.c     | 88 +++++++++++++++++++++++++++++++++++++++++++++-
     src/filelist.h     |  1 +
     tests/Makefile.am  |  3 ++
     tests/cavern.tst   |  3 +-
     tests/csprj.out    | 24 +++++++++++++
     tests/csprj.pos    |  4 +++
     tests/csprj.prj    | 33 +++++++++++++++++
     tests/csprj.svx    | 11 ++++++
     tests/csprj2.out   | 24 +++++++++++++
     tests/csprj2.pos   |  4 +++
     tests/csprj2.prj   |  1 +
     tests/csprj2.svx   | 11 ++++++
     tests/csprjbad.out | 36 +++++++++++++++++++
     tests/csprjbad.svx | 13 +++++++
     tests/csprjbom.prj |  1 +
     17 files changed, 292 insertions(+), 3 deletions(-)
     create mode 100644 tests/csprj.out
     create mode 100644 tests/csprj.pos
     create mode 100644 tests/csprj.prj
     create mode 100644 tests/csprj.svx
     create mode 100644 tests/csprj2.out
     create mode 100644 tests/csprj2.pos
     create mode 100644 tests/csprj2.prj
     create mode 100644 tests/csprj2.svx
     create mode 100644 tests/csprjbad.out
     create mode 100644 tests/csprjbad.svx
     create mode 100644 tests/csprjbom.prj
    
    diff --git a/doc/3dformat.htm b/doc/3dformat.htm
    index b6e8ab42..17546c91 100644
    a b There's no length limit on this string.  
    6060which can be passed to PROJ.  For a coordinate system with an EPSG code
    6161<code>EPSG:</code> followed by the code number can be used (we recommend
    6262using this if an EPSG code exists).  Similarly, an ESRI code can be specified
    63 with <code>ESRI:</code> followed by the code number.
     63with <code>ESRI:</code> followed by the code number.  Any other description
     64PROJ accepts may be used, such as a PROJ string, WKT or PROJJSON - this is how
     65a coordinate system based on a datum PROJ has no <code>+datum=</code> keyword
     66for gets recorded.  Since the metadata ends at a linefeed, the string must be
     67written on a single line - WKT and PROJJSON are usually pretty printed over
     68several lines, so a writer needs to join those lines (a single space in place
     69of each line break works, as neither format allows a newline inside a quoted
     70name).
    6471  <li>Survey hierarchy separator character.  Survey station names form a
    6572hierarchy, and this character separates levels in the hierarchy.  E.g.
    6673<code>161.entrance.6</code>.  Defaults to <code>.</code> if this item is not
  • doc/datafile.rst

    diff --git a/doc/datafile.rst b/doc/datafile.rst
    index 86848568..76421e9e 100644
    a b Example  
    523523       ; Output in the coordinate system used in the Totes Gebirge in Austria
    524524       *cs out custom "+proj=tmerc +lat_0=0 +lon_0=13d20 +k=1 +x_0=0 +y_0=-5200000 +ellps=bessel +towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232"
    525525
     526   ::
     527
     528       ; Output in a low distortion projection described by mammoth.prj
     529       *cs out file "mammoth.prj"
     530
    526531Description
    527532   ``*cs`` allows the coordinate systems used for fixed points and for
    528533   processed survey data to be specified.
    Description  
    560565   * ``EUR79Z30`` for UTM zone 30, EUR79 datum.  Supported since Survex
    561566     1.2.15.
    562567
     568   * ``FILE`` followed by the name of a file which describes the coordinate
     569     system (like in the example above).  The description may be a PROJ string,
     570     but the point of reading it from a file is that it can also be WKT or
     571     PROJJSON, which are made up of double quoted strings written over several
     572     lines and so are awkward to write in a ``.svx`` file.  It means a ``.prj``
     573     file such as those which accompany ESRI shapefiles can be used directly,
     574     so you can georeference a survey to match GIS data you already have.
     575
     576     WKT can also express datums which a PROJ string can't, such as
     577     NAD83(2011), ETRS89 and GDA2020.
     578
     579     The file is resolved relative to the directory which the file containing
     580     the ``*cs`` is in, and if the name as specified is not found cavern will
     581     try adding a ``.prj`` extension.  If the filename contains spaces, it must
     582     be enclosed in double quotes, and you can double the quote character to
     583     include it in the filename.
     584
     585     The lines of the file are joined with a single space, since the coordinate
     586     system is stored in the ``.3d`` file as a single line.  Neither WKT nor
     587     PROJJSON allows a newline inside a quoted name, so only insignificant
     588     whitespace is affected.
     589
     590     Supported since Survex 1.4.23.
     591
    563592   * ``IJTSK`` for the modified version of the Czechoslovak S-JTSK system
    564593     where the axes point East and North.  Supported since Survex 1.2.15.
    565594
  • src/commands.c

    diff --git a/src/commands.c b/src/commands.c
    index 1f247a1d..2e5e4fc8 100644
    a b  
    3333#include "datain.h"
    3434#include "date.h"
    3535#include "debug.h"
     36#include "filelist.h"
    3637#include "filename.h"
    3738#include "message.h"
    3839#include "netbits.h"
    typedef enum {  
    24662467    CS_EPSG,
    24672468    CS_ESRI,
    24682469    CS_EUR79Z30,
     2470    CS_FILE,
    24692471    CS_IJTSK,
    24702472    CS_IJTSK03,
    24712473    CS_JTSK,
    static const sztok cs_tab[] = {  
    24832485     {"EPSG",     CS_EPSG},     /* EPSG:<number> */
    24842486     {"ESRI",     CS_ESRI},     /* ESRI:<number> */
    24852487     {"EUR79Z30", CS_EUR79Z30},
     2488     {"FILE",     CS_FILE},     /* FILE <filename> */
    24862489     {"IJTSK",    CS_IJTSK},
    24872490     {"IJTSK03",  CS_IJTSK03},
    24882491     {"JTSK",     CS_JTSK},
    static const sztok cs_tab[] = {  
    24972500     {NULL,       CS_NONE}
    24982501};
    24992502
     2503/* Read a coordinate system description from the file FNM, as specified by
     2504 * `*cs file FILENAME`.  FP is the position of FILENAME, which any diagnostic
     2505 * is reported against.
     2506 *
     2507 * Returns the description, or NULL if the file couldn't be opened (in which
     2508 * case a diagnostic has been reported).
     2509 */
     2510static char *
     2511read_cs_from_file(const char *fnm, const filepos *fp)
     2512{
     2513   char *pth = path_from_fnm(file.filename);
     2514   char *fnm_used = NULL;
     2515   FILE *fh = fopen_portable(pth, fnm, EXT_PRJ, "rb", &fnm_used);
     2516   free(pth);
     2517   if (fh == NULL) {
     2518      set_pos(fp);
     2519      compile_diagnostic(DIAG_ERR|DIAG_STRING, /*Couldn’t open file “%s”*/1,
     2520                         fnm);
     2521      return NULL;
     2522   }
     2523
     2524   /* We store the coordinate system in the .3d file as a single line, so join
     2525    * the lines with a space, dropping blanks at the start and end of each.
     2526    * Neither WKT nor PROJJSON allows a newline inside a quoted name, so only
     2527    * insignificant whitespace is affected.
     2528    */
     2529   string cs = S_INIT;
     2530   /* s_clear() gives cs a buffer, which s_steal() needs even for an empty
     2531    * file. */
     2532   s_clear(&cs);
     2533   string blanks = S_INIT;
     2534   bool line_break = false;
     2535   for (int c = GETC(fh); c != EOF; c = GETC(fh)) {
     2536      if (c == '\n' || c == '\r') {
     2537         line_break = true;
     2538         s_clear(&blanks);
     2539         continue;
     2540      }
     2541      if (c == ' ' || c == '\t') {
     2542         /* Only keep blanks which turn out to be between two non-blanks on
     2543          * the same line. */
     2544         if (cs.len) s_appendch(&blanks, c);
     2545         continue;
     2546      }
     2547      if (cs.len) {
     2548         if (line_break) {
     2549            s_appendch(&cs, ' ');
     2550         } else {
     2551            s_appends(&cs, &blanks);
     2552         }
     2553      }
     2554      s_clear(&blanks);
     2555      line_break = false;
     2556      s_appendch(&cs, c);
     2557   }
     2558   s_free(&blanks);
     2559
     2560   if (FERROR(fh))
     2561      fatalerror_in_file(fnm_used, 0, /*Error reading file*/18);
     2562   fclose(fh);
     2563   free(fnm_used);
     2564
     2565   return s_steal(&cs);
     2566}
     2567
    25002568static void
    25012569cmd_cs(void)
    25022570{
    cmd_cs(void)  
    25642632           proj_str = s_steal(&str);
    25652633           cs_sub = 0;
    25662634           break;
     2635         case CS_FILE: {
     2636           ok_for_output = MAYBE;
     2637           get_pos(&fp);
     2638           string fnm_str = S_INIT;
     2639           read_string(&fnm_str);
     2640           proj_str = read_cs_from_file(s_str(&fnm_str), &fp);
     2641           s_free(&fnm_str);
     2642           if (!proj_str) {
     2643              skipline();
     2644              return;
     2645           }
     2646           cs_sub = 0;
     2647           break;
     2648         }
    25672649         case CS_EPSG: case CS_ESRI:
    25682650           ok_for_output = MAYBE;
    25692651           if (ch == ':' && isdigit(nextch())) {
    cmd_cs(void)  
    26352717           break;
    26362718       }
    26372719   }
    2638    if (cs_sub == INT_MIN || (cs != CS_CUSTOM && isalnum(ch))) {
     2720   if (cs_sub == INT_MIN ||
     2721       (cs != CS_CUSTOM && cs != CS_FILE && isalnum(ch))) {
    26392722      set_pos(&fp);
    26402723      compile_diagnostic(DIAG_ERR|DIAG_WORD, /*Unknown coordinate system*/434);
    26412724      skipline();
    cmd_cs(void)  
    26462729      case CS_NONE:
    26472730         break;
    26482731      case CS_CUSTOM:
     2732      case CS_FILE:
    26492733         /* proj_str already set */
    26502734         break;
    26512735      case CS_EPSG:
    cmd_cs(void)  
    27772861         /* Same as the current output projection, so valid for input. */
    27782862      } else if (pcs->proj_str && strcmp(proj_str, pcs->proj_str) == 0) {
    27792863         /* Same as the current input projection, so nothing to do! */
     2864         free(proj_str);
    27802865         return;
    27812866      } else if (ok_for_output == MAYBE) {
    27822867         /* (ok_for_output == MAYBE) also happens to indicate whether we need
    cmd_cs(void)  
    27902875                               proj_context_errno_string(PJ_DEFAULT_CTX,
    27912876                                                         proj_context_errno(PJ_DEFAULT_CTX)));
    27922877            skipline();
     2878            free(proj_str);
    27932879            return;
    27942880         }
    27952881         proj_destroy(pj);
  • src/filelist.h

    diff --git a/src/filelist.h b/src/filelist.h
    index 215cbbff..694d5d78 100644
    a b  
    2424#define EXT_SVX_MSG  "msg"
    2525#define EXT_INI      "ini"
    2626#define EXT_LOG      "log"
     27#define EXT_PRJ      "prj"
  • tests/Makefile.am

    diff --git a/tests/Makefile.am b/tests/Makefile.am
    index 51bd3a6a..e41e8f04 100644
    a b csbad.altout csbad.out csbad.svx\  
    178178csbadsdfix.altout csbadsdfix.out csbadsdfix.svx\
    179179csfeet.out csfeet.pos csfeet.svx\
    180180cslonglat.out cslonglat.svx\
     181csprj.out csprj.pos csprj.prj csprj.svx\
     182csprj2.out csprj2.pos csprj2.prj csprj2.svx\
     183csprjbad.out csprjbad.svx csprjbom.prj\
    181184omitfixaroundsolve.out omitfixaroundsolve.svx\
    182185repeatreading.svx repeatreading.out repeatreading.pos\
    183186mixedeols.out mixedeols.svx\
  • tests/cavern.tst

    diff --git a/tests/cavern.tst b/tests/cavern.tst
    index 51e4c564..662b4027 100755
    a b TESTS_=  
    121121 passage hanging_lrud equatenosuchstn surveytypo\
    122122 skipafterbadomit passagebad badreadingdotplus badcalibrate calibrate_clino\
    123123 badunits badbegin anonstn anonstnbad anonstnrev doubleinc reenterlots\
    124  cs csbad csbadsdfix csfeet cslonglat omitfixaroundsolve repeatreading\
     124 cs csbad csbadsdfix csfeet cslonglat csprj csprj2 csprjbad\
     125 omitfixaroundsolve repeatreading\
    125126 mixedeols utf8bom nonewlineateof suspectreadings cmd_data_default\
    126127 cmd_data_ignore\
    127128 quadrant_bearing bad_quadrant_bearing\
  • new file tests/csprj.out

    diff --git a/tests/csprj.out b/tests/csprj.out
    new file mode 100644
    index 00000000..c4cb8d94
    - +  
     1
     2Removing trailing traverses...
     3
     4Concatenating traverses...
     5
     6Simplifying network...
     7
     8Calculating network...
     9
     10Calculating traverses...
     11
     12Calculating trailing traverses...
     13
     14Calculating statistics...
     15
     16Approximate full range of grid convergence: 0.0dg at entrance to 0.0dg at b
     17Survey contains 3 survey stations, joined by 2 legs.
     18There are 0 loops.
     19Total length of survey legs =   55.00m (  55.00m adjusted)
     20Total plan length of survey legs =   54.87m
     21Total vertical length of survey legs =    3.49m
     22Vertical range = 2.61m (from entrance at 200.00m to a at 197.39m)
     23North-South range = 17.67m (from a at 1109.86m to b at 1092.19m)
     24East-West range = 47.55m (from b at 935.37m to entrance at 887.82m)
  • new file tests/csprj.pos

    diff --git a/tests/csprj.pos b/tests/csprj.pos
    new file mode 100644
    index 00000000..b2af0f79
    - +  
     1( Easting, Northing, Altitude )
     2(  917.71,  1109.86,   197.39 ) a
     3(  935.37,  1092.19,   198.26 ) b
     4(  887.82,  1109.86,   200.00 ) entrance
  • new file tests/csprj.prj

    diff --git a/tests/csprj.prj b/tests/csprj.prj
    new file mode 100644
    index 00000000..bb40c2e4
    - +  
     1PROJCRS["Mammoth Cave LDP",
     2    BASEGEOGCRS["NAD83(2011)",
     3        DATUM["NAD83 (National Spatial Reference System 2011)",
     4            ELLIPSOID["GRS 1980",6378137,298.257222101,
     5                LENGTHUNIT["metre",1]]],
     6        PRIMEM["Greenwich",0,
     7            ANGLEUNIT["degree",0.0174532925199433]],
     8        ID["EPSG",6318]],
     9    CONVERSION["Transverse Mercator",
     10        METHOD["Transverse Mercator",
     11            ID["EPSG",9807]],
     12        PARAMETER["Latitude of natural origin",37.1866,
     13            ANGLEUNIT["degree",0.0174532925199433],
     14            ID["EPSG",8801]],
     15        PARAMETER["Longitude of natural origin",-86.1005,
     16            ANGLEUNIT["degree",0.0174532925199433],
     17            ID["EPSG",8802]],
     18        PARAMETER["Scale factor at natural origin",1,
     19            SCALEUNIT["unity",1],
     20            ID["EPSG",8805]],
     21        PARAMETER["False easting",0,
     22            LENGTHUNIT["metre",1],
     23            ID["EPSG",8806]],
     24        PARAMETER["False northing",0,
     25            LENGTHUNIT["metre",1],
     26            ID["EPSG",8807]]],
     27    CS[Cartesian,2],
     28    AXIS["(E)",east,
     29        ORDER[1],
     30        LENGTHUNIT["metre",1]],
     31    AXIS["(N)",north,
     32        ORDER[2],
     33        LENGTHUNIT["metre",1]]]
  • new file tests/csprj.svx

    diff --git a/tests/csprj.svx b/tests/csprj.svx
    new file mode 100644
    index 00000000..f099422d
    - +  
     1; pos=yes warn=0
     2; Test *cs file FILENAME, which reads the coordinate system from a separate
     3; file.  Here that's a low distortion projection for a cave, written as WKT
     4; over several lines - a PROJ string can't express the datum it's based on.
     5*cs EPSG:6318
     6*cs out FILE csprj.prj
     7
     8*fix entrance -86.0905 37.1966 200
     9
     10entrance a 30.0 090 -05
     11a        b 25.0 135 +02
  • new file tests/csprj2.out

    diff --git a/tests/csprj2.out b/tests/csprj2.out
    new file mode 100644
    index 00000000..cac0d37f
    - +  
     1
     2Removing trailing traverses...
     3
     4Concatenating traverses...
     5
     6Simplifying network...
     7
     8Calculating network...
     9
     10Calculating traverses...
     11
     12Calculating trailing traverses...
     13
     14Calculating statistics...
     15
     16Approximate full range of grid convergence: 0.0dg at entrance to 0.0dg at b
     17Survey contains 3 survey stations, joined by 2 legs.
     18There are 0 loops.
     19Total length of survey legs =   55.00m (  55.00m adjusted)
     20Total plan length of survey legs =   54.87m
     21Total vertical length of survey legs =    3.49m
     22Vertical range = 2.61m (from entrance at 200.00m to a at 197.39m)
     23North-South range = 17.67m (from a at 41819.44m to b at 41801.78m)
     24East-West range = 47.55m (from b at 10891.01m to entrance at 10843.46m)
  • new file tests/csprj2.pos

    diff --git a/tests/csprj2.pos b/tests/csprj2.pos
    new file mode 100644
    index 00000000..7fb569af
    - +  
     1( Easting, Northing, Altitude )
     2( 10873.35, 41819.44,   197.39 ) a
     3( 10891.01, 41801.78,   198.26 ) b
     4( 10843.46, 41819.44,   200.00 ) entrance
  • new file tests/csprj2.prj

    diff --git a/tests/csprj2.prj b/tests/csprj2.prj
    new file mode 100644
    index 00000000..d89bdac5
    - +  
     1PROJCRS["Mammoth Cave LDP", BASEGEOGCRS["NAD83(2011)", DATUM["NAD83 (National Spatial Reference System 2011)", ELLIPSOID["GRS 1980",6378137,298.257222101, LENGTHUNIT["metre",1]]], PRIMEM["Greenwich",0, ANGLEUNIT["degree",0.0174532925199433]], ID["EPSG",6318]], CONVERSION["Mammoth Cave LDP TM", METHOD["Transverse Mercator", ID["EPSG",9807]], PARAMETER["Latitude of natural origin",37, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8801]], PARAMETER["Longitude of natural origin",-86.1, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8802]], PARAMETER["Scale factor at natural origin",1.0000383, SCALEUNIT["unity",1], ID["EPSG",8805]], PARAMETER["False easting",10000, LENGTHUNIT["metre",1], ID["EPSG",8806]], PARAMETER["False northing",20000, LENGTHUNIT["metre",1], ID["EPSG",8807]]], CS[Cartesian,2], AXIS["easting (E)",east, ORDER[1], LENGTHUNIT["metre",1]], AXIS["northing (N)",north, ORDER[2], LENGTHUNIT["metre",1]]]
  • new file tests/csprj2.svx

    diff --git a/tests/csprj2.svx b/tests/csprj2.svx
    new file mode 100644
    index 00000000..6785fb35
    - +  
     1; pos=yes warn=0
     2; Test that *cs file FILENAME assumes a .prj extension if the name as given
     3; doesn't exist, that the filename can be quoted, and that a description
     4; written on a single line (as ESRI .prj files are) works.
     5*cs EPSG:6318
     6*cs out FILE "csprj2"
     7
     8*fix entrance -86.0905 37.1966 200
     9
     10entrance a 30.0 090 -05
     11a        b 25.0 135 +02
  • new file tests/csprjbad.out

    diff --git a/tests/csprjbad.out b/tests/csprjbad.out
    new file mode 100644
    index 00000000..12f4acae
    - +  
     1./csprjbad.svx:3:14: error: Couldn't open file "nosuchfile.prj"
     2 *cs out FILE nosuchfile.prj
     3              ^~~~~~~~~~~~~~
     4./csprjbad.svx:5:14: error: Invalid coordinate system: Invalid PROJ string syntax
     5 *cs out FILE csprjbom.prj
     6              ^~~~~~~~~~~~
     7./csprjbad.svx:8:21: error: End of line not blank
     8 *cs FILE "csprj.prj"x
     9                     ^
     10./csprjbad.svx:11: error: The input projection is set but the output projection isn't
     11 *fix entrance -86.0905 37.1966 200
     12
     13Removing trailing traverses...
     14
     15Concatenating traverses...
     16
     17Simplifying network...
     18
     19Calculating network...
     20
     21Calculating traverses...
     22
     23Calculating trailing traverses...
     24
     25Calculating statistics...
     26
     27Survey contains 2 survey stations, joined by 1 leg.
     28There are 0 loops.
     29Total length of survey legs =   30.00m (  30.00m adjusted)
     30Total plan length of survey legs =   29.89m
     31Total vertical length of survey legs =    2.61m
     32Vertical range = 2.61m (from entrance at 200.00m to a at 197.39m)
     33North-South range = 0.00m (from a at 37.20m to a at 37.20m)
     34East-West range = 29.89m (from a at -56.20m to entrance at -86.09m)
     35
     36There were 0 warning(s) and 4 error(s) - no output files produced.
  • new file tests/csprjbad.svx

    diff --git a/tests/csprjbad.svx b/tests/csprjbad.svx
    new file mode 100644
    index 00000000..990df42e
    - +  
     1; pos=fail warn=0 error=4
     2; Test errors from *cs file FILENAME.
     3*cs out FILE nosuchfile.prj
     4; PROJ rejects a description which starts with a UTF-8 "BOM".
     5*cs out FILE csprjbom.prj
     6; Junk after the filename is reported as such, not as an unknown coordinate
     7; system.
     8*cs FILE "csprj.prj"x
     9*cs EPSG:6318
     10
     11*fix entrance -86.0905 37.1966 200
     12
     13entrance a 30.0 090 -05
  • new file tests/csprjbom.prj

    diff --git a/tests/csprjbom.prj b/tests/csprjbom.prj
    new file mode 100644
    index 00000000..1a1c8d4a
    - +  
     1PROJCRS["Mammoth Cave LDP", BASEGEOGCRS["NAD83(2011)", DATUM["NAD83 (National Spatial Reference System 2011)", ELLIPSOID["GRS 1980",6378137,298.257222101, LENGTHUNIT["metre",1]]], PRIMEM["Greenwich",0, ANGLEUNIT["degree",0.0174532925199433]], ID["EPSG",6318]], CONVERSION["Mammoth Cave LDP TM", METHOD["Transverse Mercator", ID["EPSG",9807]], PARAMETER["Latitude of natural origin",37, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8801]], PARAMETER["Longitude of natural origin",-86.1, ANGLEUNIT["degree",0.0174532925199433], ID["EPSG",8802]], PARAMETER["Scale factor at natural origin",1.0000383, SCALEUNIT["unity",1], ID["EPSG",8805]], PARAMETER["False easting",10000, LENGTHUNIT["metre",1], ID["EPSG",8806]], PARAMETER["False northing",20000, LENGTHUNIT["metre",1], ID["EPSG",8807]]], CS[Cartesian,2], AXIS["easting (E)",east, ORDER[1], LENGTHUNIT["metre",1]], AXIS["northing (N)",north, ORDER[2], LENGTHUNIT["metre",1]]]