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