Skip to content

Commit e0009ea

Browse files
authored
Merge pull request #2000 from brownag/proj-fixes1
fix: PROJ initialization and network behavior
2 parents a70e586 + bd0ff49 commit e0009ea

File tree

7 files changed

+110
-39
lines changed

7 files changed

+110
-39
lines changed

NAMESPACE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ S3method(str, SpatRaster)
2121
S3method(str, SpatExtent)
2222
S3method(str, SpatGraticule)
2323

24-
export(add_abline, add_box, add_legend, add_grid, add_mtext, ar_info, clearVSIcache, combineLevels, focalMat, extractAlong, gdal, getGDALconfig, graticule, halo, libVersion, proj_ok, setGDALconfig, map.pal, map_extent, north, sbar, terraOptions, tmpFiles, makeVRT, mem_info, free_RAM, same.crs, shade, gdalCache, fileBlocksize, vector_layers, vrt_tiles, names, round, unloadGDALdrivers)
24+
export(add_abline, add_box, add_legend, add_grid, add_mtext, ar_info, clearVSIcache, combineLevels, focalMat, extractAlong, gdal, getGDALconfig, graticule, halo, libVersion, proj_ok, projNetwork, projPaths, setGDALconfig, map.pal, map_extent, north, sbar, terraOptions, tmpFiles, makeVRT, mem_info, free_RAM, same.crs, shade, gdalCache, fileBlocksize, vector_layers, vrt_tiles, names, round, unloadGDALdrivers)
2525

2626

R/RcppExports.R

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ rgb2hex <- function(x) {
113113
.Call(`_terra_get_proj_search_paths`)
114114
}
115115

116-
.set_proj_search_paths <- function(paths) {
117-
.Call(`_terra_set_proj_search_paths`, paths)
116+
.set_proj_search_paths <- function(paths, with_proj = FALSE) {
117+
.Call(`_terra_set_proj_search_paths`, paths, with_proj)
118118
}
119119

120120
.PROJ_network <- function(enable, url) {

R/gdal.R

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,37 @@ proj_ok <- function() {
99
}
1010

1111

12+
projNetwork <- function(enable, url="") {
13+
if (missing(enable)) {
14+
as.logical(.PROJ_network(-1, ""))
15+
} else {
16+
if (enable) {
17+
out <- .PROJ_network(1, url)
18+
if (out != "") {
19+
message("PROJ network enabled. Using CDN URL: ", out)
20+
} else {
21+
message("PROJ network enabled")
22+
}
23+
} else {
24+
.PROJ_network(0, "")
25+
message("PROJ network disabled")
26+
}
27+
invisible(NULL)
28+
}
29+
}
30+
31+
32+
projPaths <- function(paths, with_proj = TRUE) {
33+
if (missing(paths)) {
34+
.get_proj_search_paths()
35+
} else {
36+
paths <- as.character(paths)
37+
.set_proj_search_paths(paths, with_proj)
38+
invisible(.get_proj_search_paths())
39+
}
40+
}
41+
42+
1243
fileBlocksize <- function(x) {
1344
v <- x@pntr$getFileBlocksize()
1445
m <- matrix(v, ncol=2)

R/zzz.R

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@
2424

2525
.gdinit <- function() {
2626
path = ""
27-
sf <- system.file("", package="terra")
28-
if (file.exists(file.path(sf, "proj/nad.lst"))) {
29-
path <- system.file("proj", package="terra")
30-
.gdalinit(path, file.path(sf, "gdal"))
31-
if ( Sys.info()["sysname"] == "Windows" ) {
32-
.set_proj_search_paths(path)
33-
}
34-
} else {
35-
.gdalinit(path, file.path(sf, "gdal"))
27+
proj_path <- system.file("proj", package="terra")
28+
gdal_path <- system.file("gdal", package="terra")
29+
30+
has_proj <- dir.exists(proj_path) && (file.exists(file.path(proj_path, "proj.db")) || file.exists(file.path(proj_path, "nad.lst")))
31+
has_gdal <- dir.exists(gdal_path)
32+
33+
if (has_proj) {
34+
projPaths(proj_path, TRUE)
3635
}
36+
37+
.gdalinit(path, ifelse(has_gdal, gdal_path, ""))
38+
3739
if (libVersion("gdal") == "3.6.0") {
3840
message("Using GDAL version 3.6.0 which was retracted because it cannot write large GPKG files")
3941
}

man/gdal.Rd

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
\alias{setGDALconfig}
99
\alias{unloadGDALdrivers}
1010
\alias{proj_ok}
11+
\alias{projNetwork}
12+
\alias{projPaths}
1113

1214

13-
\title{GDAL version, supported file formats, and cache size}
15+
\title{GDAL version, supported file formats, cache size, and PROJ coordinate transformation control}
1416

1517
\description{
1618
Set the \code{GDAL} warning level or get a \code{data.frame} with the available GDAL drivers (file formats), or, if \code{warn=NA} and \code{drivers=FALSE}, you get the version numbers of one or all of the GDAL, PROJ and GEOS libraries.
@@ -20,6 +22,10 @@ Set the \code{GDAL} warning level or get a \code{data.frame} with the available
2022
The current \code{GDAL} configuration options and obtained with \code{getGDALconfig} and changed with \code{setGDALconfig}.
2123

2224
\code{proj_ok} checks if the PROJ database with CRS definitions can be found.
25+
26+
\code{projNetwork} controls whether PROJ can access network resources for coordinate transformations. By default, network access is enabled to provide high-accuracy grid-based datum transformations where available. When disabled, PROJ falls back to \href{https://proj.org/en/stable/glossary.html#term-Ballpark-transformation}{"ballpark transformations"} (of unknown accuracy) which could lead to errors of hundreds of meters in some cases. When enabled, PROJ downloads transformation grids from \code{https://cdn.proj.org}, requiring network connectivity and valid SSL certificates. After a successful run, the files are cached locally.
27+
28+
\code{projPaths} gets or sets the search paths that PROJ uses to find coordinate system definitions and transformation grids. This allows users to specify custom locations for PROJ data files, such as when using offline installations or custom grid files. By default, it operates on PROJ search paths, but can also set GDAL search paths when \code{with_proj=FALSE}.
2329
}
2430

2531
\usage{
@@ -31,6 +37,8 @@ clearVSIcache()
3137
libVersion(lib="all", parse=FALSE)
3238
unloadGDALdrivers(x)
3339
proj_ok()
40+
projNetwork(enable, url="")
41+
projPaths(paths, with_proj = TRUE)
3442
}
3543

3644
\arguments{
@@ -42,21 +50,32 @@ proj_ok()
4250
\item{value}{character. value for GDAL configuration option. Use "" to reset it to its default value}
4351
\item{lib}{character. "gdal", "proj", or "geos", or any other value to get the versions numbers of all three}
4452
\item{parse}{logical. Should the version be parsed into three numerical values (major, minor and sub versions)?}
45-
\item{x}{character. Drivers names such as "GTiff" to be unloaded. Or "" to reload all drivers}
53+
\item{x}{character. Drivers names such as "GTiff" to be unloaded. Or "" to reload all drivers}
54+
\item{enable}{logical. If \code{TRUE}, enable PROJ network access for high-accuracy grid-based transformations. If \code{FALSE}, disable network access and use ballpark transformations. If missing, return the current network status}
55+
\item{url}{character. Optional URL for PROJ network endpoint. If empty string (default), uses PROJ's default network settings (\code{https://cdn.proj.org})}
56+
\item{paths}{character. Vector of file paths to directories containing PROJ data files. If missing, returns the current search paths}
57+
\item{with_proj}{logical. If \code{TRUE} (default), set PROJ search paths. If \code{FALSE}, set GDAL search paths}
4658
}
4759
4860
4961
\seealso{\code{\link{describe}} for file-level metadata "GDALinfo"}
5062
5163
\value{
52-
character
64+
character vector of search paths. When setting paths, the result is returned invisibly.
5365
}
5466
5567
\examples{
5668
gdal()
5769
gdal(2)
5870
head(gdal(drivers=TRUE))
5971
libVersion("all", TRUE)
72+
projNetwork()
73+
projPaths()
74+
projPaths(c("/custom/proj/path"))
75+
}
76+
77+
\note{
78+
While some spatial analyses may not be greatly affected by PROJ network settings (ballpark vs. grid-based transformations), the differences can be significant, especially when a transformation involves a shift in datum between different coordinate reference systems. For applications requiring high positional accuracy, ensure network access is enabled or grids are locally available. Grids can be pre-downloaded using the \code{projsync} utility or installed via system packages, such as \code{proj-data} on Ubuntu/Debian systems. Downloaded grids are cached locally and then reused for subsequent transformations.
6079
}
6180
6281
\keyword{spatial}

src/RcppExports.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,23 +319,24 @@ BEGIN_RCPP
319319
END_RCPP
320320
}
321321
// set_proj_search_paths
322-
bool set_proj_search_paths(std::vector<std::string> paths);
323-
RcppExport SEXP _terra_set_proj_search_paths(SEXP pathsSEXP) {
322+
bool set_proj_search_paths(std::vector<std::string> paths, bool with_proj);
323+
RcppExport SEXP _terra_set_proj_search_paths(SEXP pathsSEXP, SEXP with_projSEXP) {
324324
BEGIN_RCPP
325325
Rcpp::RObject rcpp_result_gen;
326326
Rcpp::RNGScope rcpp_rngScope_gen;
327327
Rcpp::traits::input_parameter< std::vector<std::string> >::type paths(pathsSEXP);
328-
rcpp_result_gen = Rcpp::wrap(set_proj_search_paths(paths));
328+
Rcpp::traits::input_parameter< bool >::type with_proj(with_projSEXP);
329+
rcpp_result_gen = Rcpp::wrap(set_proj_search_paths(paths, with_proj));
329330
return rcpp_result_gen;
330331
END_RCPP
331332
}
332333
// PROJ_network
333-
std::string PROJ_network(bool enable, std::string url);
334+
std::string PROJ_network(int enable, std::string url);
334335
RcppExport SEXP _terra_PROJ_network(SEXP enableSEXP, SEXP urlSEXP) {
335336
BEGIN_RCPP
336337
Rcpp::RObject rcpp_result_gen;
337338
Rcpp::RNGScope rcpp_rngScope_gen;
338-
Rcpp::traits::input_parameter< bool >::type enable(enableSEXP);
339+
Rcpp::traits::input_parameter< int >::type enable(enableSEXP);
339340
Rcpp::traits::input_parameter< std::string >::type url(urlSEXP);
340341
rcpp_result_gen = Rcpp::wrap(PROJ_network(enable, url));
341342
return rcpp_result_gen;
@@ -480,7 +481,7 @@ static const R_CallMethodDef CallEntries[] = {
480481
{"_terra_setGDALCacheSizeMB", (DL_FUNC) &_terra_setGDALCacheSizeMB, 2},
481482
{"_terra_getGDALCacheSizeMB", (DL_FUNC) &_terra_getGDALCacheSizeMB, 1},
482483
{"_terra_get_proj_search_paths", (DL_FUNC) &_terra_get_proj_search_paths, 0},
483-
{"_terra_set_proj_search_paths", (DL_FUNC) &_terra_set_proj_search_paths, 1},
484+
{"_terra_set_proj_search_paths", (DL_FUNC) &_terra_set_proj_search_paths, 2},
484485
{"_terra_PROJ_network", (DL_FUNC) &_terra_PROJ_network, 2},
485486
{"_terra_removeDriver", (DL_FUNC) &_terra_removeDriver, 1},
486487
{"_terra_pearson_cor", (DL_FUNC) &_terra_pearson_cor, 3},

src/RcppFunctions.cpp

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -434,14 +434,14 @@ void gdal_init(std::string projpath, std::string datapath) {
434434
//GDAL_NETCDF_IGNORE_XY_AXIS_NAME_CHECKS
435435

436436
//GDALregistred = true;
437-
#if GDAL_VERSION_MAJOR >= 3
438-
#ifdef PROJ_6
439437
if (!projpath.empty()) {
440-
const char *cp = projpath.c_str();
441-
proj_context_set_search_paths(PJ_DEFAULT_CTX, 1, &cp);
442-
}
443-
#endif
438+
#if GDAL_VERSION_NUM >= 3000000
439+
std::vector<char *> cpaths(2);
440+
cpaths[0] = (char *)projpath.c_str();
441+
cpaths[1] = NULL;
442+
OSRSetPROJSearchPaths(cpaths.data());
444443
#endif
444+
}
445445
#ifdef PROJ_71
446446
#ifndef __EMSCRIPTEN__
447447
proj_context_set_enable_network(PJ_DEFAULT_CTX, 1);
@@ -567,36 +567,54 @@ std::vector<std::string> get_proj_search_paths() {
567567

568568

569569
// [[Rcpp::export(name = ".set_proj_search_paths")]]
570-
bool set_proj_search_paths(std::vector<std::string> paths) {
570+
bool set_proj_search_paths(std::vector<std::string> paths, bool with_proj = false) {
571571
if (paths.empty()) {
572572
return false;
573573
}
574+
if (with_proj) {
575+
// Set for PROJ library
576+
if (paths.size() == 1) {
577+
const char *cp = paths[0].c_str();
578+
proj_context_set_search_paths(PJ_DEFAULT_CTX, 1, &cp);
579+
}
580+
return true;
581+
} else {
582+
// Set for GDAL
574583
#if GDAL_VERSION_NUM >= 3000000
575-
std::vector <char *> cpaths(paths.size()+1);
576-
for (size_t i = 0; i < paths.size(); i++) {
577-
cpaths[i] = (char *) (paths[i].c_str());
578-
}
579-
cpaths[cpaths.size()-1] = NULL;
580-
OSRSetPROJSearchPaths(cpaths.data());
581-
return true;
584+
std::vector <char *> cpaths(paths.size()+1);
585+
for (size_t i = 0; i < paths.size(); i++) {
586+
cpaths[i] = (char *) (paths[i].c_str());
587+
}
588+
cpaths[cpaths.size()-1] = NULL;
589+
OSRSetPROJSearchPaths(cpaths.data());
590+
return true;
582591
#else
583-
return false;
592+
return false;
584593
#endif
594+
}
585595
}
586596

587597

588598
// [[Rcpp::export(name = ".PROJ_network")]]
589-
std::string PROJ_network(bool enable, std::string url) {
599+
std::string PROJ_network(int enable, std::string url) {
590600
std::string s = "";
591601
#ifdef PROJ_71
592-
if (enable) {
602+
if (enable == -1) { // get current status
603+
return std::to_string(proj_context_is_network_enabled(PJ_DEFAULT_CTX));
604+
} else if (enable == 1) {
593605
proj_context_set_enable_network(PJ_DEFAULT_CTX, 1);
606+
#if GDAL_VERSION_NUM >= 3040000
607+
OSRSetPROJEnableNetwork(1);
608+
#endif
594609
if (url.size() > 5) {
595610
proj_context_set_url_endpoint(PJ_DEFAULT_CTX, url.c_str());
596611
}
597612
s = proj_context_get_url_endpoint(PJ_DEFAULT_CTX);
598-
} else { // disable:
613+
} else if (enable == 0) { // disable:
599614
proj_context_set_enable_network(PJ_DEFAULT_CTX, 0);
615+
#if GDAL_VERSION_NUM >= 3040000
616+
OSRSetPROJEnableNetwork(0);
617+
#endif
600618
}
601619
#endif
602620
return s;

0 commit comments

Comments
 (0)