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