GRASS 8 Programmer's Manual 8.6.0dev(2026)-09ad2796ff
Loading...
Searching...
No Matches
convert.c
Go to the documentation of this file.
1/*!
2 \file lib/proj/convert.c
3
4 \brief GProj Library - Functions for manipulating co-ordinate
5 system representations
6
7 SPDX-FileCopyrightText: 2003-2018 GRASS Development Team
8 SPDX-License-Identifier: GPL-2.0-or-later
9
10 \author Paul Kelly, Frank Warmerdam, Markus Metz
11 */
12
13#include <grass/config.h>
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <math.h>
19#include <grass/gis.h>
20#include <grass/gprojects.h>
21#include <grass/glocale.h>
22
23#include <cpl_csv.h>
24#include "local_proto.h"
25
26/* GRASS relative location of OGR co-ordinate system lookup tables */
27#define CSVDIR "/proj/ogr_csv"
28
29static void DatumNameMassage(char **);
30
31/* from proj-5.0.0/src/pj_units.c */
32struct gpj_units {
33 char *id; /* units keyword */
34 char *to_meter; /* multiply by value to get meters */
35 char *name; /* comments */
36 double factor; /* to_meter factor in actual numbers */
37};
38
39struct gpj_units gpj_units[] = {
40 {"km", "1000.", "Kilometer", 1000.0},
41 {"m", "1.", "Meter", 1.0},
42 {"dm", "1/10", "Decimeter", 0.1},
43 {"cm", "1/100", "Centimeter", 0.01},
44 {"mm", "1/1000", "Millimeter", 0.001},
45 {"kmi", "1852.0", "International Nautical Mile", 1852.0},
46 {"in", "0.0254", "International Inch", 0.0254},
47 {"ft", "0.3048", "International Foot", 0.3048},
48 {"yd", "0.9144", "International Yard", 0.9144},
49 {"mi", "1609.344", "International Statute Mile", 1609.344},
50 {"fath", "1.8288", "International Fathom", 1.8288},
51 {"ch", "20.1168", "International Chain", 20.1168},
52 {"link", "0.201168", "International Link", 0.201168},
53 {"us-in", "1./39.37", "U.S. Surveyor's Inch", 0.0254},
54 {"us-ft", "0.304800609601219", "U.S. Surveyor's Foot", 0.304800609601219},
55 {"us-yd", "0.914401828803658", "U.S. Surveyor's Yard", 0.914401828803658},
56 {"us-ch", "20.11684023368047", "U.S. Surveyor's Chain", 20.11684023368047},
57 {"us-mi", "1609.347218694437", "U.S. Surveyor's Statute Mile",
58 1609.347218694437},
59 {"ind-yd", "0.91439523", "Indian Yard", 0.91439523},
60 {"ind-ft", "0.30479841", "Indian Foot", 0.30479841},
61 {"ind-ch", "20.11669506", "Indian Chain", 20.11669506},
62 {NULL, NULL, NULL, 0.0}};
63
64static char *grass_to_wkt(const struct Key_Value *proj_info,
65 const struct Key_Value *proj_units,
66 const struct Key_Value *proj_epsg, int esri_style,
67 int prettify)
68{
70 char *wkt, *local_wkt;
71
72 hSRS = GPJ_grass_to_osr2(proj_info, proj_units, proj_epsg);
73
74 if (hSRS == NULL)
75 return NULL;
76
77 if (esri_style)
79
80 if (prettify)
81 OSRExportToPrettyWkt(hSRS, &wkt, 0);
82 else
83 OSRExportToWkt(hSRS, &wkt);
84
85 local_wkt = G_store(wkt);
86 CPLFree(wkt);
88
89 return local_wkt;
90}
91
92/*!
93 * \brief Converts a GRASS co-ordinate system representation to WKT style.
94 *
95 * Takes a GRASS co-ordinate system as specified by two sets of
96 * key/value pairs derived from the PROJ_INFO and PROJ_UNITS files,
97 * and converts it to the 'Well Known Text' format.
98 *
99 * \param proj_info Set of GRASS PROJ_INFO key/value pairs
100 * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
101 * \param esri_style boolean Output ESRI-style WKT (Use OSRMorphToESRI()
102 * function provided by OGR library)
103 * \param prettify boolean Use linebreaks and indents to 'prettify' output
104 * WKT string (Use OSRExportToPrettyWkt() function in OGR)
105 *
106 * \return Pointer to a string containing the co-ordinate system in
107 * WKT format
108 * \return NULL on error
109 */
110char *GPJ_grass_to_wkt(const struct Key_Value *proj_info,
111 const struct Key_Value *proj_units, int esri_style,
112 int prettify)
113{
114 return grass_to_wkt(proj_info, proj_units, NULL, esri_style, prettify);
115}
116
117/*!
118 * \brief Converts a GRASS co-ordinate system representation to WKT
119 * style. EPSG code is preferred if available.
120 *
121 * Takes a GRASS co-ordinate system as specified key/value pairs
122 * derived from the PROJ_EPSG file. TOWGS84 parameter is scanned
123 * from PROJ_INFO file and appended to co-ordinate system definition
124 * imported from EPSG code by GDAL library. PROJ_UNITS file is
125 * ignored. The function converts it to the 'Well Known Text' format.
126 *
127 * \todo Merge with GPJ_grass_to_wkt() in GRASS 8.
128 *
129 * \param proj_info Set of GRASS PROJ_INFO key/value pairs
130 * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
131 * \param proj_epsg Set of GRASS PROJ_EPSG key/value pairs
132 * \param esri_style boolean Output ESRI-style WKT (Use OSRMorphToESRI()
133 * function provided by OGR library)
134 * \param prettify boolean Use linebreaks and indents to 'prettify' output
135 * WKT string (Use OSRExportToPrettyWkt() function in OGR)
136 *
137 * \return Pointer to a string containing the co-ordinate system in
138 * WKT format
139 * \return NULL on error
140 */
141char *GPJ_grass_to_wkt2(const struct Key_Value *proj_info,
142 const struct Key_Value *proj_units,
143 const struct Key_Value *proj_epsg, int esri_style,
144 int prettify)
145{
146 return grass_to_wkt(proj_info, proj_units, proj_epsg, esri_style, prettify);
147}
148
149/*!
150 * \brief Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object.
151 *
152 * \param proj_info Set of GRASS PROJ_INFO key/value pairs
153 * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
154 *
155 * \return OGRSpatialReferenceH object representing the co-ordinate system
156 * defined by proj_info and proj_units or NULL if it fails
157 */
159 const struct Key_Value *proj_units)
160{
161 struct pj_info pjinfo;
162 char *proj4, *proj4mod, *wkt, *modwkt, *startmod, *lastpart;
165 struct gpj_datum dstruct;
166 struct gpj_ellps estruct;
167 size_t len;
168 const char *ellpskv, *unit, *unfact;
169 char *ellps, *ellpslong, *datum, *params, *towgs84, *datumlongname, *start,
170 *end;
171 const char *sysname, *osrunit;
172 double a, es, rf;
173 int haveparams = 0;
174
175 if ((proj_info == NULL) || (proj_units == NULL))
176 return NULL;
177
179
180 /* create PROJ structure from GRASS key/value pairs */
181 if (pj_get_kv(&pjinfo, proj_info, proj_units) < 0) {
182 G_warning(_("Unable parse GRASS PROJ_INFO file"));
183 return NULL;
184 }
185
186 /* fetch the PROJ definition */
187 /* TODO: get the PROJ definition as used by pj_get_kv() */
188 if ((proj4 = pjinfo.def) == NULL) {
189 G_warning(_("Unable get PROJ.4-style parameter string"));
190 return NULL;
191 }
193
194 unit = G_find_key_value("unit", proj_units);
195 unfact = G_find_key_value("meters", proj_units);
196 if (unfact != NULL && (strcmp(pjinfo.proj, "ll") != 0))
197 G_asprintf(&proj4mod, "%s +to_meter=%s", proj4, unfact);
198 else
200
201 /* create GDAL OSR from proj string */
203 G_warning(_("OGR can't parse PROJ.4-style parameter string: "
204 "%s (OGR Error code was %d)"),
206 return NULL;
207 }
209
210 /* this messes up PROJCS versus GEOGCS!
211 sysname = G_find_key_value("name", proj_info);
212 if (sysname)
213 OSRSetProjCS(hSRS, sysname);
214 */
215
216 if ((errcode = OSRExportToWkt(hSRS, &wkt)) != OGRERR_NONE) {
217 G_warning(_("OGR can't get WKT-style parameter string "
218 "(OGR Error code was %d)"),
219 errcode);
220 return NULL;
221 }
222
223 ellpskv = G_find_key_value("ellps", proj_info);
224 GPJ__get_ellipsoid_params(proj_info, &a, &es, &rf);
225 haveparams = GPJ__get_datum_params(proj_info, &datum, &params);
226
227 if (ellpskv != NULL)
228 ellps = G_store(ellpskv);
229 else
230 ellps = NULL;
231
232 if ((datum == NULL) || (GPJ_get_datum_by_name(datum, &dstruct) < 0)) {
233 datumlongname = G_store("unknown");
234 if (ellps == NULL)
235 ellps = G_store("unnamed");
236 }
237 else {
238 datumlongname = G_store(dstruct.longname);
239 if (ellps == NULL)
240 ellps = G_store(dstruct.ellps);
242 }
243 G_debug(3, "GPJ_grass_to_osr: datum: <%s>", datum);
244 G_free(datum);
245 if (GPJ_get_ellipsoid_by_name(ellps, &estruct) > 0) {
246 ellpslong = G_store(estruct.longname);
247 DatumNameMassage(&ellpslong);
249 }
250 else
251 ellpslong = G_store(ellps);
252
253 startmod = strstr(wkt, "GEOGCS");
254 lastpart = strstr(wkt, "PRIMEM");
255 len = strlen(wkt) - strlen(startmod);
256 wkt[len] = '\0';
257 if (haveparams == 2) {
258 /* Only put datum params into the WKT if they were specifically
259 * specified in PROJ_INFO */
260 char *paramkey, *paramvalue;
261
262 paramkey = strtok(params, "=");
263 paramvalue = params + strlen(paramkey) + 1;
264 if (G_strcasecmp(paramkey, "towgs84") == 0)
265 G_asprintf(&towgs84, ",TOWGS84[%s]", paramvalue);
266 else
267 towgs84 = G_store("");
268 G_free(params);
269 }
270 else
271 towgs84 = G_store("");
272
273 sysname = OSRGetAttrValue(hSRS, "PROJCS", 0);
274 if (sysname == NULL) {
275 /* Not a projected co-ordinate system */
276 start = G_store("");
277 end = G_store("");
278 }
279 else {
280 if ((strcmp(sysname, "unnamed") == 0) &&
281 (G_find_key_value("name", proj_info) != NULL))
282 G_asprintf(&start, "PROJCS[\"%s\",",
283 G_find_key_value("name", proj_info));
284 else
285 start = G_store(wkt);
286
287 osrunit = OSRGetAttrValue(hSRS, "UNIT", 0);
288
289 if ((unfact == NULL) || (G_strcasecmp(osrunit, "unknown") != 0))
290 end = G_store("");
291 else {
292 char *buff;
293 double unfactf = atof(unfact);
294
295 G_asprintf(&buff, ",UNIT[\"%s\",", osrunit);
296
297 startmod = strstr(lastpart, buff);
298 len = strlen(lastpart) - strlen(startmod);
299 lastpart[len] = '\0';
300 G_free(buff);
301
302 if (unit == NULL)
303 unit = "unknown";
304 G_asprintf(&end, ",UNIT[\"%s\",%.16g]]", unit, unfactf);
305 }
306 }
309 &modwkt,
310 "%sGEOGCS[\"%s\",DATUM[\"%s\",SPHEROID[\"%s\",%.16g,%.16g]%s],%s%s",
311 start, ellps, datumlongname, ellpslong, a, rf, towgs84, lastpart, end);
313 G_free(modwkt);
314
315 CPLFree(wkt);
316 G_free(start);
317 G_free(ellps);
321 G_free(end);
322
323 return hSRS2;
324}
325
326/*!
327 * \brief Converts a GRASS co-ordinate system to an
328 * OGRSpatialReferenceH object. EPSG code is preferred if available.
329 *
330 * The co-ordinate system definition is imported from EPSG (by GDAL)
331 * definition if available. TOWGS84 parameter is scanned from
332 * PROJ_INFO file and appended to co-ordinate system definition. If
333 * EPSG code is not available, PROJ_INFO file is used as
334 * GPJ_grass_to_osr() does.
335
336 * \todo Merge with GPJ_grass_to_osr() in GRASS 8.
337 *
338 * \param proj_info Set of GRASS PROJ_INFO key/value pairs
339 * \param proj_units Set of GRASS PROJ_UNIT key/value pairs
340 * \param proj_epsg Set of GRASS PROJ_EPSG key/value pairs
341 *
342 * \return OGRSpatialReferenceH object representing the co-ordinate system
343 * defined by proj_info and proj_units or NULL if it fails
344 */
346 const struct Key_Value *proj_units,
347 const struct Key_Value *proj_epsg)
348{
349 int epsgcode = 0;
350
351 if (proj_epsg) {
352 const char *epsgstr = G_find_key_value("epsg", proj_epsg);
353
354 if (epsgstr)
356 }
357
358 if (epsgcode) {
359 const char *towgs84;
361
363
365
366 /* take +towgs84 from projinfo file if defined */
367 towgs84 = G_find_key_value("towgs84", proj_info);
368 if (towgs84) {
369 char **tokens;
370 int i;
371 double df[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
372
373 tokens = G_tokenize(towgs84, ",");
374
375 for (i = 0; i < G_number_of_tokens(tokens); i++)
376 df[i] = atof(tokens[i]);
378
379 OSRSetTOWGS84(hSRS, df[0], df[1], df[2], df[3], df[4], df[5],
380 df[6]);
381 }
382
383 return hSRS;
384 }
385
386 return GPJ_grass_to_osr(proj_info, proj_units);
387}
388
389/*!
390 * \brief Converts an OGRSpatialReferenceH object to a GRASS co-ordinate system.
391 *
392 * \param cellhd Pointer to a GRASS Cell_head structure that will have its
393 * projection-related members populated with appropriate
394 * values
395 * \param projinfo Pointer to a pointer which will have a GRASS
396 * Key_Value structure allocated containing a set of GRASS
397 * PROJ_INFO values
398 * \param projunits Pointer to a pointer which will have a GRASS Key_Value
399 * structure allocated containing a set of GRASS PROJ_UNITS
400 * values.
401 * \param hSRS1 OGRSpatialReferenceH object containing the
402 * co-ordinate system to be converted
403 * \param datumtrans Index number of datum parameter set to use, 0 to leave
404 * unspecified
405 *
406 * \return 2 if a projected or lat/long co-ordinate system has been
407 * defined
408 * \return 1 if an unreferenced XY co-ordinate system has
409 * been defined
410 */
411int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
412 struct Key_Value **projunits, OGRSpatialReferenceH hSRS1,
413 int datumtrans)
414{
416 char *pszProj4 = NULL, *pszRemaining;
417 char *pszProj = NULL;
418 const char *pszProjCS = NULL;
419 char *datum = NULL;
420 char *proj4_unit = NULL;
421 struct gpj_datum dstruct;
422 const char *ograttr;
425
426 *projinfo = NULL;
427 *projunits = NULL;
428
429 hSRS = hSRS1;
430
431 if (hSRS == NULL)
432 goto default_to_xy;
433
434 /* Set finder function for locating OGR csv co-ordinate system tables */
435 /* SetCSVFilenameHook(GPJ_set_csv_loc); */
436
437 /* Hopefully this doesn't do any harm if it wasn't in ESRI format
438 * to start with... */
440
441 *projinfo = G_create_key_value();
443
444 /* use proj4 definition from EXTENSION attribute if existing */
445 ograttr = OSRGetAttrValue(hSRS, "EXTENSION", 0);
446 if (ograttr && *ograttr && strcmp(ograttr, "PROJ4") == 0) {
447 ograttr = OSRGetAttrValue(hSRS, "EXTENSION", 1);
448 G_debug(3, "proj4 extension:");
449 G_debug(3, "%s", ograttr);
450
451 if (ograttr && *ograttr) {
452 char *proj4ext;
454
457
458 /* test */
460 G_warning(_("Updating spatial reference with embedded proj4 "
461 "definition failed. "
462 "Proj4 definition: <%s>"),
463 proj4ext);
465 }
466 else {
467 /* use new OGR spatial reference defined with embedded proj4
468 * string */
469 /* TODO: replace warning with important_message once confirmed
470 * working */
471 G_warning(_("Updating spatial reference with embedded proj4 "
472 "definition"));
473
474 /* --------------------------------------------------------------------
475 */
476 /* Derive the user name for the coordinate system. */
477 /* --------------------------------------------------------------------
478 */
479 pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0);
480 if (!pszProjCS)
481 pszProjCS = OSRGetAttrValue(hSRS, "GEOGCS", 0);
482
483 if (pszProjCS) {
484 G_set_key_value("name", pszProjCS, *projinfo);
485 }
486 else if (pszProj) {
487 char path[4095];
488 char name[80];
489
490 /* use name of the projection as name for the coordinate
491 * system */
492
493 snprintf(path, sizeof(path), "%s/proj/projections",
494 G_etc_dir());
496 sizeof(name)) > 0)
497 G_set_key_value("name", name, *projinfo);
498 else
499 G_set_key_value("name", pszProj, *projinfo);
500 }
501
502 /* the original hSRS1 is left as is, ok? */
503 hSRS = hSRS2;
505 }
507 }
508 }
509
510 /* -------------------------------------------------------------------- */
511 /* Set cellhd for well known coordinate systems. */
512 /* -------------------------------------------------------------------- */
514 goto default_to_xy;
515
516 if (cellhd) {
517 int bNorth;
518
519 if (OSRIsGeographic(hSRS)) {
520 cellhd->proj = PROJECTION_LL;
521 cellhd->zone = 0;
522 }
523 else if (OSRGetUTMZone(hSRS, &bNorth) != 0) {
524 cellhd->proj = PROJECTION_UTM;
525 cellhd->zone = OSRGetUTMZone(hSRS, &bNorth);
526 if (!bNorth)
527 cellhd->zone *= -1;
528 }
529 else {
530 cellhd->proj = PROJECTION_OTHER;
531 cellhd->zone = 0;
532 }
533 }
534
535 /* -------------------------------------------------------------------- */
536 /* Get the coordinate system definition in PROJ.4 format. */
537 /* -------------------------------------------------------------------- */
539 goto default_to_xy;
540
541 /* -------------------------------------------------------------------- */
542 /* Parse the PROJ.4 string into key/value pairs. Do a bit of */
543 /* extra work to "GRASSify" the result. */
544 /* -------------------------------------------------------------------- */
547
548 /* Create "local" copy of proj4 string so we can modify and free it
549 * using GRASS functions */
553 while ((pszRemaining = strstr(pszRemaining, "+")) != NULL) {
554 char *pszToken, *pszValue;
555
556 pszRemaining++;
557
558 /* Advance pszRemaining to end of this token[=value] pair */
560 while (*pszRemaining != ' ' && *pszRemaining != '\0')
561 pszRemaining++;
562
563 if (*pszRemaining == ' ') {
564 *pszRemaining = '\0';
565 pszRemaining++;
566 }
567
568 /* parse token, and value */
569 if (strstr(pszToken, "=") != NULL) {
570 pszValue = strstr(pszToken, "=");
571 *pszValue = '\0';
572 pszValue++;
573 }
574 else
575 pszValue = "defined";
576
577 /* projection name */
578 if (G_strcasecmp(pszToken, "proj") == 0) {
579 /* The ll projection is known as longlat in PROJ.4 */
580 if (G_strcasecmp(pszValue, "longlat") == 0)
581 pszValue = "ll";
582
584 }
585
586 /* Ellipsoid and datum handled separately below */
587 if (G_strcasecmp(pszToken, "ellps") == 0 ||
588 G_strcasecmp(pszToken, "a") == 0 ||
589 G_strcasecmp(pszToken, "b") == 0 ||
590 G_strcasecmp(pszToken, "es") == 0 ||
591 G_strcasecmp(pszToken, "rf") == 0 ||
592 G_strcasecmp(pszToken, "datum") == 0) {
594 continue;
595 }
596
597 /* We will handle units separately */
598 if (G_strcasecmp(pszToken, "to_meter") == 0)
599 continue;
600
601 if (G_strcasecmp(pszToken, "units") == 0) {
603 continue;
604 }
605
607 }
608 if (!pszProj)
609 G_warning(_("No projection name! Projection parameters likely to be "
610 "meaningless."));
611
612 /* -------------------------------------------------------------------- */
613 /* Derive the user name for the coordinate system. */
614 /* -------------------------------------------------------------------- */
615 if (!G_find_key_value("name", *projinfo)) {
616 pszProjCS = OSRGetAttrValue(hSRS, "PROJCS", 0);
617 if (!pszProjCS)
618 pszProjCS = OSRGetAttrValue(hSRS, "GEOGCS", 0);
619
620 if (pszProjCS) {
621 G_set_key_value("name", pszProjCS, *projinfo);
622 }
623 else if (pszProj) {
624 char path[4095];
625 char name[80];
626
627 /* use name of the projection as name for the coordinate system */
628
629 snprintf(path, sizeof(path), "%s/proj/projections", G_etc_dir());
631 sizeof(name)) > 0)
632 G_set_key_value("name", name, *projinfo);
633 else
634 G_set_key_value("name", pszProj, *projinfo);
635 }
636 }
637
638 /* -------------------------------------------------------------------- */
639 /* Find the GRASS datum name and choose parameters either */
640 /* interactively or not. */
641 /* -------------------------------------------------------------------- */
642
643 {
644 const char *pszDatumNameConst;
645 struct datum_list *list, *listhead;
646 char *dum1, *dum2, *pszDatumName;
648
651 else
653
654 if (pszDatumNameConst) {
655 /* Need to make a new copy of the string so we don't mess
656 * around with the memory inside the OGRSpatialReferenceH? */
657
659 DatumNameMassage(&pszDatumName);
660 G_debug(3, "GPJ_osr_to_grass: pszDatumNameConst: <%s>",
662
664
665 while (list != NULL) {
666 if (G_strcasecmp(pszDatumName, list->longname) == 0) {
667 datum = G_store(list->name);
668 break;
669 }
670 list = list->next;
671 }
673
674 if (datum == NULL) {
675 if (paramspresent < 2)
676 /* Only give warning if no parameters present */
677 G_debug(1,
678 "Datum <%s> not recognised by GRASS and no "
679 "parameters found",
681 }
682 else {
683 G_set_key_value("datum", datum, *projinfo);
684
685 if (paramspresent < 2) {
686 /* If no datum parameters were imported from the OSR
687 * object then we should use the set specified by datumtrans
688 */
689 char *params, *chosenparams = NULL;
690 int paramsets;
691
692 paramsets =
694
695 if (paramsets < 0)
696 G_debug(1,
697 "Datum <%s> apparently recognised by GRASS but "
698 "no parameters found. "
699 "You may want to look into this.",
700 datum);
701 else if (datumtrans > paramsets) {
702
703 G_debug(
704 1,
705 "Invalid transformation number %d; valid range is "
706 "1 to %d. "
707 "Leaving datum transform parameters unspecified.",
709 datumtrans = 0;
710 }
711
712 if (paramsets > 0) {
713 struct gpj_datum_transform_list *tlist, *old;
714
715 tlist = GPJ_get_datum_transform_by_name(datum);
716
717 if (tlist != NULL) {
718 do {
719 if (tlist->count == datumtrans)
720 chosenparams = G_store(tlist->params);
721 old = tlist;
722 tlist = tlist->next;
724 } while (tlist != NULL);
725 }
726 }
727
728 if (chosenparams != NULL) {
729 char *paramkey, *paramvalue;
730
735 }
736
737 if (paramsets > 0)
738 G_free(params);
739 }
740 }
742 }
743 }
744
745 /* -------------------------------------------------------------------- */
746 /* Determine an appropriate GRASS ellipsoid name if possible, or */
747 /* else just put a and es values into PROJ_INFO */
748 /* -------------------------------------------------------------------- */
749
750 if ((datum != NULL) && (GPJ_get_datum_by_name(datum, &dstruct) > 0)) {
751 /* Use ellps name associated with datum */
752 G_set_key_value("ellps", dstruct.ellps, *projinfo);
754 G_free(datum);
755 }
756 else if (!use_proj_extension) {
757 /* If we can't determine the ellipsoid from the datum, derive it
758 * directly from "SPHEROID" parameters in WKT */
759 const char *pszSemiMajor = OSRGetAttrValue(hSRS, "SPHEROID", 1);
760 const char *pszInvFlat = OSRGetAttrValue(hSRS, "SPHEROID", 2);
761
762 if (pszSemiMajor != NULL && pszInvFlat != NULL) {
763 char *ellps = NULL;
764 struct ellps_list *list, *listhead;
765 double a = atof(pszSemiMajor), invflat = atof(pszInvFlat), flat;
766 double es;
767
768 /* Allow for incorrect WKT describing a sphere where InvFlat
769 * is given as 0 rather than inf */
770 if (invflat > 0)
771 flat = 1 / invflat;
772 else
773 flat = 0;
774
775 es = flat * (2.0 - flat);
776
778
779 while (list != NULL) {
780 /* Try and match a and es against GRASS defined ellipsoids;
781 * accept first one that matches. These numbers were found
782 * by trial and error and could be fine-tuned, or possibly
783 * a direct comparison of IEEE floating point values used. */
784 if ((a == list->a || fabs(a - list->a) < 0.1 ||
785 fabs(1 - a / list->a) < 0.0000001) &&
786 ((es == 0 && list->es == 0) ||
787 /* Special case for sphere */
788 (invflat == list->rf ||
789 fabs(invflat - list->rf) < 0.0000001))) {
790 ellps = G_store(list->name);
791 break;
792 }
793 list = list->next;
794 }
795 if (listhead != NULL)
797
798 if (ellps == NULL) {
799 /* If we weren't able to find a matching ellps name, set
800 * a and es values directly from WKT-derived data */
801 char es_str[100];
802
803 G_set_key_value("a", (char *)pszSemiMajor, *projinfo);
804
805 snprintf(es_str, sizeof(es_str), "%.16g", es);
806 G_set_key_value("es", es_str, *projinfo);
807 }
808 else {
809 /* else specify the GRASS ellps name for readability */
810 G_set_key_value("ellps", ellps, *projinfo);
811 G_free(ellps);
812 }
813 }
814 }
815 else if (use_proj_extension) {
816 double a, es, rf;
817
818 if (GPJ__get_ellipsoid_params(temp_projinfo_ext, &a, &es, &rf)) {
819 char parmstr[100];
820
821 snprintf(parmstr, sizeof(parmstr), "%.16g", a);
822 G_set_key_value("a", parmstr, *projinfo);
823 snprintf(parmstr, sizeof(parmstr), "%.16g", es);
824 G_set_key_value("es", parmstr, *projinfo);
825 }
826 }
827
828 /* -------------------------------------------------------------------- */
829 /* Finally append the detailed projection parameters to the end */
830 /* -------------------------------------------------------------------- */
831
832 {
833 int i;
834
835 for (i = 0; i < temp_projinfo->nitems; i++)
837 *projinfo);
838
840 }
842
844
845 /* -------------------------------------------------------------------- */
846 /* Set the linear units. */
847 /* -------------------------------------------------------------------- */
848 *projunits = G_create_key_value();
849
850 if (OSRIsGeographic(hSRS)) {
851 /* We assume degrees ... someday we will be wrong! */
852 G_set_key_value("unit", "degree", *projunits);
853 G_set_key_value("units", "degrees", *projunits);
854 G_set_key_value("meters", "1.0", *projunits);
855 }
856 else {
857 char szFormatBuf[256];
858 char *pszUnitsName = NULL;
859 double dfToMeters;
861
863
864 /* the unit name can be arbitrary: the following can be the same
865 * us-ft (proj.4 keyword)
866 * U.S. Surveyor's Foot (proj.4 name)
867 * US survey foot (WKT)
868 * Foot_US (WKT)
869 */
870
871 /* Workaround for the most obvious case when unit name is unknown */
872 if ((G_strcasecmp(pszUnitsName, "unknown") == 0) && (dfToMeters == 1.))
873 G_asprintf(&pszUnitsName, "meter");
874
875 if ((G_strcasecmp(pszUnitsName, "metre") == 0))
876 G_asprintf(&pszUnitsName, "meter");
877 if ((G_strcasecmp(pszUnitsName, "kilometre") == 0))
878 G_asprintf(&pszUnitsName, "kilometer");
879
880 if (dfToMeters != 1. && proj4_unit) {
881 int i;
882
883 i = 0;
884 while (gpj_units[i].id != NULL) {
885 if (strcmp(proj4_unit, gpj_units[i].id) == 0) {
887 break;
888 }
889 i++;
890 }
891 }
892
893 G_set_key_value("unit", pszUnitsName, *projunits);
894
895 /* Attempt at plural formation (WKT format doesn't store plural
896 * form of unit name) */
900 if (G_strcasecmp(pszStringEnd, "foot") == 0) {
901 /* Special case for foot - change two o's to e's */
902 pszStringEnd[1] = 'e';
903 pszStringEnd[2] = 'e';
904 }
905 else if (G_strcasecmp(pszStringEnd, "inch") == 0) {
906 /* Special case for inch - add es */
907 pszStringEnd[4] = 'e';
908 pszStringEnd[5] = 's';
909 pszStringEnd[6] = '\0';
910 }
911 else {
912 /* For anything else add an s at the end */
913 pszStringEnd[4] = 's';
914 pszStringEnd[5] = '\0';
915 }
916
917 G_set_key_value("units", pszUnitsPlural, *projunits);
919
920 snprintf(szFormatBuf, sizeof(szFormatBuf), "%.16g", dfToMeters);
921 G_set_key_value("meters", szFormatBuf, *projunits);
922 }
923
924 if (hSRS != hSRS1)
926
927 return 2;
928
929 /* -------------------------------------------------------------------- */
930 /* Fallback to returning an ungeoreferenced definition. */
931 /* -------------------------------------------------------------------- */
933 if (cellhd != NULL) {
934 cellhd->proj = PROJECTION_XY;
935 cellhd->zone = 0;
936 }
937 if (*projinfo)
938 G_free_key_value(*projinfo);
939
940 *projinfo = NULL;
941 *projunits = NULL;
942
943 if (hSRS != NULL && hSRS != hSRS1)
945
946 return 1;
947}
948
949/*!
950 * \brief Converts a WKT projection description to a GRASS co-ordinate system.
951 *
952 * \param cellhd Pointer to a GRASS Cell_head structure that will have its
953 * projection-related members populated with appropriate
954 * values \param projinfo Pointer to a pointer which will have a GRASS
955 * Key_Value structure allocated containing a set of GRASS PROJ_INFO values
956 * \param projunits Pointer to a pointer which will have a GRASS Key_Value
957 * structure allocated containing a set of GRASS PROJ_UNITS
958 * values \param wkt Well-known Text (WKT) description of the
959 * co-ordinate system to be converted \param datumtrans Index number of datum
960 * parameter set to use, 0 to leave unspecified
961 *
962 * \return 2 if a projected or lat/long co-ordinate system has been
963 * defined
964 * \return 1 if an unreferenced XY co-ordinate system has
965 * been defined
966 * \return -1 on error
967 */
968int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
969 struct Key_Value **projunits, const char *wkt,
970 int datumtrans)
971{
972 int retval;
973
974 if (wkt == NULL)
975 retval =
976 GPJ_osr_to_grass(cellhd, projinfo, projunits, NULL, datumtrans);
977 else {
979
980 /* Set finder function for locating OGR csv co-ordinate system tables */
981 /* SetCSVFilenameHook(GPJ_set_csv_loc); */
982
984 retval =
985 GPJ_osr_to_grass(cellhd, projinfo, projunits, hSRS, datumtrans);
987 }
988
989 return retval;
990}
991
992/* GPJ_set_csv_loc()
993 * 'finder function' for use with OGR SetCSVFilenameHook() function */
994
995const char *GPJ_set_csv_loc(const char *name)
996{
997 const char *g_etc_dir = G_etc_dir();
998 static char *buf = NULL;
999
1000 if (buf != NULL)
1001 G_free(buf);
1002
1003 G_asprintf(&buf, "%s%s/%s", g_etc_dir, CSVDIR, name);
1004
1005 return buf;
1006}
1007
1008/* The list below is only for files that use a non-standard name for a
1009 * datum that is already supported in GRASS. The number of entries must be even;
1010 * they are all in pairs. The first one in the pair is the non-standard name;
1011 * the second is the GRASS/GDAL name. If a name appears more than once (as for
1012 * European_Terrestrial_Reference_System_1989) then it means there was more
1013 * than one non-standard name for it that needs to be accounted for.
1014 *
1015 * N.B. The order of these pairs is different from that in
1016 * ogr/ogrfromepsg.cpp in the GDAL source tree! GRASS uses the EPSG
1017 * names in its WKT representation except WGS_1984 and WGS_1972 as
1018 * these shortened versions seem to be standard.
1019 * Below order:
1020 * the equivalent name comes first in the pair, and
1021 * the EPSG name (as used in the GRASS datum.table file) comes second.
1022 *
1023 * The datum parameters are stored in
1024 * ../gis/datum.table # 3 parameters
1025 * ../gis/datumtransform.table # 7 parameters (requires entry in datum.table)
1026 *
1027 * Hint: use GDAL's "testepsg" to identify the canonical name, e.g.
1028 * testepsg epsg:4674
1029 */
1030
1031static const char *papszDatumEquiv[] = {
1032 "Militar_Geographische_Institute",
1033 "Militar_Geographische_Institut",
1034 "World_Geodetic_System_1984",
1035 "WGS_1984",
1036 "World_Geodetic_System_1972",
1037 "WGS_1972",
1038 "European_Terrestrial_Reference_System_89",
1039 "European_Terrestrial_Reference_System_1989",
1040 "European_Reference_System_1989",
1041 "European_Terrestrial_Reference_System_1989",
1042 "ETRS_1989",
1043 "European_Terrestrial_Reference_System_1989",
1044 "ETRS89",
1045 "European_Terrestrial_Reference_System_1989",
1046 "ETRF_1989",
1047 "European_Terrestrial_Reference_System_1989",
1048 "NZGD_2000",
1049 "New_Zealand_Geodetic_Datum_2000",
1050 "Monte_Mario_Rome",
1051 "Monte_Mario",
1052 "MONTROME",
1053 "Monte_Mario",
1054 "Campo_Inchauspe_1969",
1055 "Campo_Inchauspe",
1056 "S_JTSK",
1057 "System_Jednotne_Trigonometricke_Site_Katastralni",
1058 "S_JTSK_Ferro",
1059 "Militar_Geographische_Institut",
1060 "Potsdam_Datum_83",
1061 "Deutsches_Hauptdreiecksnetz",
1062 "Rauenberg_Datum_83",
1063 "Deutsches_Hauptdreiecksnetz",
1064 "South_American_1969",
1065 "South_American_Datum_1969",
1066 "International_Terrestrial_Reference_Frame_1992",
1067 "ITRF92",
1068 "ITRF_1992",
1069 "ITRF92",
1070 NULL};
1071
1072/************************************************************************/
1073/* OGREPSGDatumNameMassage() */
1074/* */
1075/* Massage an EPSG datum name into WMT format. Also transform */
1076/* specific exception cases into WKT versions. */
1077
1078/************************************************************************/
1079
1080static void DatumNameMassage(char **ppszDatum)
1081{
1082 int i, j;
1083 char *pszDatum = *ppszDatum;
1084
1085 G_debug(3, "DatumNameMassage: Raw string found <%s>", (char *)pszDatum);
1086 /* -------------------------------------------------------------------- */
1087 /* Translate non-alphanumeric values to underscores. */
1088 /* -------------------------------------------------------------------- */
1089 for (i = 0; pszDatum[i] != '\0'; i++) {
1090 if (!(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') &&
1091 !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') &&
1092 !(pszDatum[i] >= '0' && pszDatum[i] <= '9')) {
1093 pszDatum[i] = '_';
1094 }
1095 }
1096
1097 /* -------------------------------------------------------------------- */
1098 /* Remove repeated and trailing underscores. */
1099 /* -------------------------------------------------------------------- */
1100 for (i = 1, j = 0; pszDatum[i] != '\0'; i++) {
1101 if (pszDatum[j] == '_' && pszDatum[i] == '_')
1102 continue;
1103
1104 pszDatum[++j] = pszDatum[i];
1105 }
1106 if (pszDatum[j] == '_')
1107 pszDatum[j] = '\0';
1108 else
1109 pszDatum[j + 1] = '\0';
1110
1111 /* -------------------------------------------------------------------- */
1112 /* Search for datum equivalences. Specific massaged names get */
1113 /* mapped to OpenGIS specified names. */
1114 /* -------------------------------------------------------------------- */
1115 G_debug(3, "DatumNameMassage: Search for datum equivalences of <%s>",
1116 (char *)pszDatum);
1117 for (i = 0; papszDatumEquiv[i] != NULL; i += 2) {
1118 if (EQUAL(*ppszDatum, papszDatumEquiv[i])) {
1119 G_free(*ppszDatum);
1120 *ppszDatum = G_store(papszDatumEquiv[i + 1]);
1121 break;
1122 }
1123 }
1124}
#define NULL
Definition ccmath.h:32
#define CSVDIR
Definition convert.c:27
char * GPJ_grass_to_wkt(const struct Key_Value *proj_info, const struct Key_Value *proj_units, int esri_style, int prettify)
Converts a GRASS co-ordinate system representation to WKT style.
Definition convert.c:110
struct gpj_units gpj_units[]
Definition convert.c:39
OGRSpatialReferenceH GPJ_grass_to_osr2(const struct Key_Value *proj_info, const struct Key_Value *proj_units, const struct Key_Value *proj_epsg)
Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object. EPSG code is preferred if avai...
Definition convert.c:345
int GPJ_osr_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo, struct Key_Value **projunits, OGRSpatialReferenceH hSRS1, int datumtrans)
Converts an OGRSpatialReferenceH object to a GRASS co-ordinate system.
Definition convert.c:411
int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo, struct Key_Value **projunits, const char *wkt, int datumtrans)
Converts a WKT projection description to a GRASS co-ordinate system.
Definition convert.c:968
char * GPJ_grass_to_wkt2(const struct Key_Value *proj_info, const struct Key_Value *proj_units, const struct Key_Value *proj_epsg, int esri_style, int prettify)
Converts a GRASS co-ordinate system representation to WKT style. EPSG code is preferred if available.
Definition convert.c:141
OGRSpatialReferenceH GPJ_grass_to_osr(const struct Key_Value *proj_info, const struct Key_Value *proj_units)
Converts a GRASS co-ordinate system to an OGRSpatialReferenceH object.
Definition convert.c:158
const char * GPJ_set_csv_loc(const char *name)
Definition convert.c:995
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
void G_warning(const char *,...) __attribute__((format(printf
#define G_malloc(n)
Definition defs/gis.h:136
void G_free_key_value(struct Key_Value *)
Free allocated Key_Value structure.
Definition key_value1.c:102
void G_set_key_value(const char *, const char *, struct Key_Value *)
Set value for given key.
Definition key_value1.c:37
char ** G_tokenize(const char *, const char *)
Tokenize string.
Definition gis/token.c:45
int G_lookup_key_value_from_file(const char *, const char *, char[], int)
Look up for key in file.
Definition key_value4.c:44
void G_free_tokens(char **)
Free memory allocated to tokens.
Definition gis/token.c:195
int G_asprintf(char **, const char *,...) __attribute__((format(printf
struct Key_Value * G_create_key_value(void)
Allocate and initialize Key_Value structure.
Definition key_value1.c:21
const char * G_etc_dir(void)
int G_number_of_tokens(char **)
Return number of tokens.
Definition gis/token.c:176
const char * G_find_key_value(const char *, const struct Key_Value *)
Find given key (case sensitive)
Definition key_value1.c:83
int int G_strcasecmp(const char *, const char *)
String compare ignoring case (upper or lower)
Definition strings.c:46
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:86
int G_debug(int, const char *,...) __attribute__((format(printf
void GPJ_free_datum(struct gpj_datum *)
Free the memory used for the strings in a gpj_datum struct.
Definition proj/datum.c:393
int GPJ_get_ellipsoid_by_name(const char *, struct gpj_ellps *)
Looks up ellipsoid in ellipsoid table and returns the a, e2 parameters for the ellipsoid.
Definition ellipse.c:156
int GPJ_get_datum_by_name(const char *, struct gpj_datum *)
Look up a string in datum.table file to see if it is a valid datum name and if so place its informati...
Definition proj/datum.c:35
void GPJ_free_ellps(struct gpj_ellps *)
Free ellipsoid data structure.
Definition ellipse.c:299
int GPJ_get_default_datum_params_by_name(const char *, char **)
"Last resort" function to retrieve a "default" set of datum parameters for a datum (N....
Definition proj/datum.c:83
int GPJ__get_datum_params(const struct Key_Value *, char **, char **)
Extract the datum transformation-related parameters from a set of general PROJ_INFO parameters.
Definition proj/datum.c:170
int pj_get_kv(struct pj_info *, const struct Key_Value *, const struct Key_Value *)
Create a pj_info struct Co-ordinate System definition from a set of PROJ_INFO / PROJ_UNITS-style key-...
Definition get_proj.c:57
int GPJ__get_ellipsoid_params(const struct Key_Value *, double *, double *, double *)
Get the ellipsoid parameters from proj keys structure.
Definition ellipse.c:71
void GPJ_free_datum_transform(struct gpj_datum_transform_list *)
Free the memory used by a gpj_datum_transform_list struct.
Definition proj/datum.c:320
struct gpj_datum_transform_list * GPJ_get_datum_transform_by_name(const char *)
Internal function to find all possible sets of transformation parameters for a particular datum.
Definition proj/datum.c:234
struct ellps_list * read_ellipsoid_table(int fatal)
Definition ellipse.c:220
void free_ellps_list(struct ellps_list *elist)
Definition ellipse.c:306
#define PROJECTION_OTHER
Projection code - other projection (other then noted above)
Definition gis.h:130
#define PROJECTION_XY
Projection code - XY coordinate system (unreferenced data)
Definition gis.h:122
#define PROJECTION_UTM
Projection code - UTM.
Definition gis.h:124
#define PROJECTION_LL
Projection code - Latitude-Longitude.
Definition gis.h:128
#define _(str)
Definition glocale.h:10
const char * name
Definition named_colr.c:6
#define strcpy
Definition parson.c:66
void free_datum_list(struct datum_list *dstruct)
Free the memory used by a datum_list linked list structure.
Definition proj/datum.c:407
struct datum_list * read_datum_table(void)
Read the current GRASS datum.table from disk and store in memory.
Definition proj/datum.c:339
#define EQUAL
Definition sqlp.tab.h:110
2D/3D raster map header (used also for region)
Definition gis.h:445
int zone
Projection zone (UTM)
Definition gis.h:479
int proj
Projection code.
Definition gis.h:477
struct gpj_datum_transform_list * next
Definition gprojects.h:65
double es
Definition gprojects.h:72
double a
Definition gprojects.h:72
double rf
Definition gprojects.h:72
Definition manage.h:4
Definition path.h:15
char * wkt
Definition gprojects.h:45