GRASS 8 Programmer's Manual 8.6.0dev(2026)-c83afef6d3
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 "/etc/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/etc/proj/projections",
494 G_gisbase());
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/etc/proj/projections",
630 G_gisbase());
632 sizeof(name)) > 0)
633 G_set_key_value("name", name, *projinfo);
634 else
635 G_set_key_value("name", pszProj, *projinfo);
636 }
637 }
638
639 /* -------------------------------------------------------------------- */
640 /* Find the GRASS datum name and choose parameters either */
641 /* interactively or not. */
642 /* -------------------------------------------------------------------- */
643
644 {
645 const char *pszDatumNameConst;
646 struct datum_list *list, *listhead;
647 char *dum1, *dum2, *pszDatumName;
649
652 else
654
655 if (pszDatumNameConst) {
656 /* Need to make a new copy of the string so we don't mess
657 * around with the memory inside the OGRSpatialReferenceH? */
658
660 DatumNameMassage(&pszDatumName);
661 G_debug(3, "GPJ_osr_to_grass: pszDatumNameConst: <%s>",
663
665
666 while (list != NULL) {
667 if (G_strcasecmp(pszDatumName, list->longname) == 0) {
668 datum = G_store(list->name);
669 break;
670 }
671 list = list->next;
672 }
674
675 if (datum == NULL) {
676 if (paramspresent < 2)
677 /* Only give warning if no parameters present */
678 G_debug(1,
679 "Datum <%s> not recognised by GRASS and no "
680 "parameters found",
682 }
683 else {
684 G_set_key_value("datum", datum, *projinfo);
685
686 if (paramspresent < 2) {
687 /* If no datum parameters were imported from the OSR
688 * object then we should use the set specified by datumtrans
689 */
690 char *params, *chosenparams = NULL;
691 int paramsets;
692
693 paramsets =
695
696 if (paramsets < 0)
697 G_debug(1,
698 "Datum <%s> apparently recognised by GRASS but "
699 "no parameters found. "
700 "You may want to look into this.",
701 datum);
702 else if (datumtrans > paramsets) {
703
704 G_debug(
705 1,
706 "Invalid transformation number %d; valid range is "
707 "1 to %d. "
708 "Leaving datum transform parameters unspecified.",
710 datumtrans = 0;
711 }
712
713 if (paramsets > 0) {
714 struct gpj_datum_transform_list *tlist, *old;
715
716 tlist = GPJ_get_datum_transform_by_name(datum);
717
718 if (tlist != NULL) {
719 do {
720 if (tlist->count == datumtrans)
721 chosenparams = G_store(tlist->params);
722 old = tlist;
723 tlist = tlist->next;
725 } while (tlist != NULL);
726 }
727 }
728
729 if (chosenparams != NULL) {
730 char *paramkey, *paramvalue;
731
736 }
737
738 if (paramsets > 0)
739 G_free(params);
740 }
741 }
743 }
744 }
745
746 /* -------------------------------------------------------------------- */
747 /* Determine an appropriate GRASS ellipsoid name if possible, or */
748 /* else just put a and es values into PROJ_INFO */
749 /* -------------------------------------------------------------------- */
750
751 if ((datum != NULL) && (GPJ_get_datum_by_name(datum, &dstruct) > 0)) {
752 /* Use ellps name associated with datum */
753 G_set_key_value("ellps", dstruct.ellps, *projinfo);
755 G_free(datum);
756 }
757 else if (!use_proj_extension) {
758 /* If we can't determine the ellipsoid from the datum, derive it
759 * directly from "SPHEROID" parameters in WKT */
760 const char *pszSemiMajor = OSRGetAttrValue(hSRS, "SPHEROID", 1);
761 const char *pszInvFlat = OSRGetAttrValue(hSRS, "SPHEROID", 2);
762
763 if (pszSemiMajor != NULL && pszInvFlat != NULL) {
764 char *ellps = NULL;
765 struct ellps_list *list, *listhead;
766 double a = atof(pszSemiMajor), invflat = atof(pszInvFlat), flat;
767 double es;
768
769 /* Allow for incorrect WKT describing a sphere where InvFlat
770 * is given as 0 rather than inf */
771 if (invflat > 0)
772 flat = 1 / invflat;
773 else
774 flat = 0;
775
776 es = flat * (2.0 - flat);
777
779
780 while (list != NULL) {
781 /* Try and match a and es against GRASS defined ellipsoids;
782 * accept first one that matches. These numbers were found
783 * by trial and error and could be fine-tuned, or possibly
784 * a direct comparison of IEEE floating point values used. */
785 if ((a == list->a || fabs(a - list->a) < 0.1 ||
786 fabs(1 - a / list->a) < 0.0000001) &&
787 ((es == 0 && list->es == 0) ||
788 /* Special case for sphere */
789 (invflat == list->rf ||
790 fabs(invflat - list->rf) < 0.0000001))) {
791 ellps = G_store(list->name);
792 break;
793 }
794 list = list->next;
795 }
796 if (listhead != NULL)
798
799 if (ellps == NULL) {
800 /* If we weren't able to find a matching ellps name, set
801 * a and es values directly from WKT-derived data */
802 char es_str[100];
803
804 G_set_key_value("a", (char *)pszSemiMajor, *projinfo);
805
806 snprintf(es_str, sizeof(es_str), "%.16g", es);
807 G_set_key_value("es", es_str, *projinfo);
808 }
809 else {
810 /* else specify the GRASS ellps name for readability */
811 G_set_key_value("ellps", ellps, *projinfo);
812 G_free(ellps);
813 }
814 }
815 }
816 else if (use_proj_extension) {
817 double a, es, rf;
818
819 if (GPJ__get_ellipsoid_params(temp_projinfo_ext, &a, &es, &rf)) {
820 char parmstr[100];
821
822 snprintf(parmstr, sizeof(parmstr), "%.16g", a);
823 G_set_key_value("a", parmstr, *projinfo);
824 snprintf(parmstr, sizeof(parmstr), "%.16g", es);
825 G_set_key_value("es", parmstr, *projinfo);
826 }
827 }
828
829 /* -------------------------------------------------------------------- */
830 /* Finally append the detailed projection parameters to the end */
831 /* -------------------------------------------------------------------- */
832
833 {
834 int i;
835
836 for (i = 0; i < temp_projinfo->nitems; i++)
838 *projinfo);
839
841 }
843
845
846 /* -------------------------------------------------------------------- */
847 /* Set the linear units. */
848 /* -------------------------------------------------------------------- */
849 *projunits = G_create_key_value();
850
851 if (OSRIsGeographic(hSRS)) {
852 /* We assume degrees ... someday we will be wrong! */
853 G_set_key_value("unit", "degree", *projunits);
854 G_set_key_value("units", "degrees", *projunits);
855 G_set_key_value("meters", "1.0", *projunits);
856 }
857 else {
858 char szFormatBuf[256];
859 char *pszUnitsName = NULL;
860 double dfToMeters;
862
864
865 /* the unit name can be arbitrary: the following can be the same
866 * us-ft (proj.4 keyword)
867 * U.S. Surveyor's Foot (proj.4 name)
868 * US survey foot (WKT)
869 * Foot_US (WKT)
870 */
871
872 /* Workaround for the most obvious case when unit name is unknown */
873 if ((G_strcasecmp(pszUnitsName, "unknown") == 0) && (dfToMeters == 1.))
874 G_asprintf(&pszUnitsName, "meter");
875
876 if ((G_strcasecmp(pszUnitsName, "metre") == 0))
877 G_asprintf(&pszUnitsName, "meter");
878 if ((G_strcasecmp(pszUnitsName, "kilometre") == 0))
879 G_asprintf(&pszUnitsName, "kilometer");
880
881 if (dfToMeters != 1. && proj4_unit) {
882 int i;
883
884 i = 0;
885 while (gpj_units[i].id != NULL) {
886 if (strcmp(proj4_unit, gpj_units[i].id) == 0) {
888 break;
889 }
890 i++;
891 }
892 }
893
894 G_set_key_value("unit", pszUnitsName, *projunits);
895
896 /* Attempt at plural formation (WKT format doesn't store plural
897 * form of unit name) */
901 if (G_strcasecmp(pszStringEnd, "foot") == 0) {
902 /* Special case for foot - change two o's to e's */
903 pszStringEnd[1] = 'e';
904 pszStringEnd[2] = 'e';
905 }
906 else if (G_strcasecmp(pszStringEnd, "inch") == 0) {
907 /* Special case for inch - add es */
908 pszStringEnd[4] = 'e';
909 pszStringEnd[5] = 's';
910 pszStringEnd[6] = '\0';
911 }
912 else {
913 /* For anything else add an s at the end */
914 pszStringEnd[4] = 's';
915 pszStringEnd[5] = '\0';
916 }
917
918 G_set_key_value("units", pszUnitsPlural, *projunits);
920
921 snprintf(szFormatBuf, sizeof(szFormatBuf), "%.16g", dfToMeters);
922 G_set_key_value("meters", szFormatBuf, *projunits);
923 }
924
925 if (hSRS != hSRS1)
927
928 return 2;
929
930 /* -------------------------------------------------------------------- */
931 /* Fallback to returning an ungeoreferenced definition. */
932 /* -------------------------------------------------------------------- */
934 if (cellhd != NULL) {
935 cellhd->proj = PROJECTION_XY;
936 cellhd->zone = 0;
937 }
938 if (*projinfo)
939 G_free_key_value(*projinfo);
940
941 *projinfo = NULL;
942 *projunits = NULL;
943
944 if (hSRS != NULL && hSRS != hSRS1)
946
947 return 1;
948}
949
950/*!
951 * \brief Converts a WKT projection description to a GRASS co-ordinate system.
952 *
953 * \param cellhd Pointer to a GRASS Cell_head structure that will have its
954 * projection-related members populated with appropriate
955 * values \param projinfo Pointer to a pointer which will have a GRASS
956 * Key_Value structure allocated containing a set of GRASS PROJ_INFO values
957 * \param projunits Pointer to a pointer which will have a GRASS Key_Value
958 * structure allocated containing a set of GRASS PROJ_UNITS
959 * values \param wkt Well-known Text (WKT) description of the
960 * co-ordinate system to be converted \param datumtrans Index number of datum
961 * parameter set to use, 0 to leave unspecified
962 *
963 * \return 2 if a projected or lat/long co-ordinate system has been
964 * defined
965 * \return 1 if an unreferenced XY co-ordinate system has
966 * been defined
967 * \return -1 on error
968 */
969int GPJ_wkt_to_grass(struct Cell_head *cellhd, struct Key_Value **projinfo,
970 struct Key_Value **projunits, const char *wkt,
971 int datumtrans)
972{
973 int retval;
974
975 if (wkt == NULL)
976 retval =
977 GPJ_osr_to_grass(cellhd, projinfo, projunits, NULL, datumtrans);
978 else {
980
981 /* Set finder function for locating OGR csv co-ordinate system tables */
982 /* SetCSVFilenameHook(GPJ_set_csv_loc); */
983
985 retval =
986 GPJ_osr_to_grass(cellhd, projinfo, projunits, hSRS, datumtrans);
988 }
989
990 return retval;
991}
992
993/* GPJ_set_csv_loc()
994 * 'finder function' for use with OGR SetCSVFilenameHook() function */
995
996const char *GPJ_set_csv_loc(const char *name)
997{
998 const char *gisbase = G_gisbase();
999 static char *buf = NULL;
1000
1001 if (buf != NULL)
1002 G_free(buf);
1003
1004 G_asprintf(&buf, "%s%s/%s", gisbase, CSVDIR, name);
1005
1006 return buf;
1007}
1008
1009/* The list below is only for files that use a non-standard name for a
1010 * datum that is already supported in GRASS. The number of entries must be even;
1011 * they are all in pairs. The first one in the pair is the non-standard name;
1012 * the second is the GRASS/GDAL name. If a name appears more than once (as for
1013 * European_Terrestrial_Reference_System_1989) then it means there was more
1014 * than one non-standard name for it that needs to be accounted for.
1015 *
1016 * N.B. The order of these pairs is different from that in
1017 * ogr/ogrfromepsg.cpp in the GDAL source tree! GRASS uses the EPSG
1018 * names in its WKT representation except WGS_1984 and WGS_1972 as
1019 * these shortened versions seem to be standard.
1020 * Below order:
1021 * the equivalent name comes first in the pair, and
1022 * the EPSG name (as used in the GRASS datum.table file) comes second.
1023 *
1024 * The datum parameters are stored in
1025 * ../gis/datum.table # 3 parameters
1026 * ../gis/datumtransform.table # 7 parameters (requires entry in datum.table)
1027 *
1028 * Hint: use GDAL's "testepsg" to identify the canonical name, e.g.
1029 * testepsg epsg:4674
1030 */
1031
1032static const char *papszDatumEquiv[] = {
1033 "Militar_Geographische_Institute",
1034 "Militar_Geographische_Institut",
1035 "World_Geodetic_System_1984",
1036 "WGS_1984",
1037 "World_Geodetic_System_1972",
1038 "WGS_1972",
1039 "European_Terrestrial_Reference_System_89",
1040 "European_Terrestrial_Reference_System_1989",
1041 "European_Reference_System_1989",
1042 "European_Terrestrial_Reference_System_1989",
1043 "ETRS_1989",
1044 "European_Terrestrial_Reference_System_1989",
1045 "ETRS89",
1046 "European_Terrestrial_Reference_System_1989",
1047 "ETRF_1989",
1048 "European_Terrestrial_Reference_System_1989",
1049 "NZGD_2000",
1050 "New_Zealand_Geodetic_Datum_2000",
1051 "Monte_Mario_Rome",
1052 "Monte_Mario",
1053 "MONTROME",
1054 "Monte_Mario",
1055 "Campo_Inchauspe_1969",
1056 "Campo_Inchauspe",
1057 "S_JTSK",
1058 "System_Jednotne_Trigonometricke_Site_Katastralni",
1059 "S_JTSK_Ferro",
1060 "Militar_Geographische_Institut",
1061 "Potsdam_Datum_83",
1062 "Deutsches_Hauptdreiecksnetz",
1063 "Rauenberg_Datum_83",
1064 "Deutsches_Hauptdreiecksnetz",
1065 "South_American_1969",
1066 "South_American_Datum_1969",
1067 "International_Terrestrial_Reference_Frame_1992",
1068 "ITRF92",
1069 "ITRF_1992",
1070 "ITRF92",
1071 NULL};
1072
1073/************************************************************************/
1074/* OGREPSGDatumNameMassage() */
1075/* */
1076/* Massage an EPSG datum name into WMT format. Also transform */
1077/* specific exception cases into WKT versions. */
1078
1079/************************************************************************/
1080
1081static void DatumNameMassage(char **ppszDatum)
1082{
1083 int i, j;
1084 char *pszDatum = *ppszDatum;
1085
1086 G_debug(3, "DatumNameMassage: Raw string found <%s>", (char *)pszDatum);
1087 /* -------------------------------------------------------------------- */
1088 /* Translate non-alphanumeric values to underscores. */
1089 /* -------------------------------------------------------------------- */
1090 for (i = 0; pszDatum[i] != '\0'; i++) {
1091 if (!(pszDatum[i] >= 'A' && pszDatum[i] <= 'Z') &&
1092 !(pszDatum[i] >= 'a' && pszDatum[i] <= 'z') &&
1093 !(pszDatum[i] >= '0' && pszDatum[i] <= '9')) {
1094 pszDatum[i] = '_';
1095 }
1096 }
1097
1098 /* -------------------------------------------------------------------- */
1099 /* Remove repeated and trailing underscores. */
1100 /* -------------------------------------------------------------------- */
1101 for (i = 1, j = 0; pszDatum[i] != '\0'; i++) {
1102 if (pszDatum[j] == '_' && pszDatum[i] == '_')
1103 continue;
1104
1105 pszDatum[++j] = pszDatum[i];
1106 }
1107 if (pszDatum[j] == '_')
1108 pszDatum[j] = '\0';
1109 else
1110 pszDatum[j + 1] = '\0';
1111
1112 /* -------------------------------------------------------------------- */
1113 /* Search for datum equivalences. Specific massaged names get */
1114 /* mapped to OpenGIS specified names. */
1115 /* -------------------------------------------------------------------- */
1116 G_debug(3, "DatumNameMassage: Search for datum equivalences of <%s>",
1117 (char *)pszDatum);
1118 for (i = 0; papszDatumEquiv[i] != NULL; i += 2) {
1119 if (EQUAL(*ppszDatum, papszDatumEquiv[i])) {
1120 G_free(*ppszDatum);
1121 *ppszDatum = G_store(papszDatumEquiv[i + 1]);
1122 break;
1123 }
1124 }
1125}
#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:969
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:996
void G_free(void *)
Free allocated memory.
Definition gis/alloc.c:145
void G_warning(const char *,...) __attribute__((format(printf
const char * G_gisbase(void)
Get full path name of the top level module directory.
Definition gisbase.c:39
#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
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:45
char * G_store(const char *)
Copy string to allocated memory.
Definition strings.c:85
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:128
#define PROJECTION_XY
Projection code - XY coordinate system (unreferenced data)
Definition gis.h:120
#define PROJECTION_UTM
Projection code - UTM.
Definition gis.h:122
#define PROJECTION_LL
Projection code - Latitude-Longitude.
Definition gis.h:126
#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:443
int zone
Projection zone (UTM)
Definition gis.h:477
int proj
Projection code.
Definition gis.h:475
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