@@ -93,3 +93,117 @@ ctx <- function() {
9393
9494global_ctx <- new.env(parent = emptyenv())
9595global_ctx $ ctx <- NULL
96+
97+
98+
99+ # ' Configure PROJ
100+ # '
101+ # ' Performs a runtime configuration of PROJ, which can be used in place of
102+ # ' a build-time linked version of PROJ or to add in support if PROJ was
103+ # ' not linked at build time.
104+ # '
105+ # ' @param preset One of:
106+ # ' - `"homebrew"`: Look for PROJ installed by Homebrew. This is the easiest
107+ # ' option on MacOS.
108+ # ' - `"system"`: Look for PROJ in the platform library load path (e.g.,
109+ # ' after installing system proj on Linux).
110+ # ' - `"auto"`: Try all presets in the order listed above, issuing a warning
111+ # ' if none can be configured.
112+ # ' @param shared_library An absolute or relative path to a shared library
113+ # ' valid for the platform.
114+ # ' @param database_path A path to proj.db
115+ # ' @param search_path A path to the data files required by PROJ for some
116+ # ' transforms.
117+ # '
118+ # ' @returns NULL, invisibly
119+ # ' @export
120+ # '
121+ # ' @examples
122+ # ' sd_configure_proj("auto")
123+ # '
124+ sd_configure_proj <- function (preset = NULL ,
125+ shared_library = NULL ,
126+ database_path = NULL ,
127+ search_path = NULL ) {
128+ if (! is.null(preset )) {
129+ switch (preset ,
130+ homebrew = {
131+ configure_proj_prefix(Sys.getenv(" HOMEBREW_PREFIX" , " /opt/homebrew" ))
132+ return (invisible (NULL ))
133+ },
134+ system = {
135+ configure_proj_system()
136+ return (invisible (NULL ))
137+ },
138+ auto = {
139+ presets <- c(" homebrew" , " system" )
140+ errors <- c()
141+ for (preset in presets ) {
142+ maybe_err <- try(sd_configure_proj(preset ), silent = TRUE )
143+ if (! inherits(maybe_err , " try-error" )) {
144+ return (invisible (NULL ))
145+ } else {
146+ errors <- c(errors , sprintf(" %s: %s" , preset , maybe_err ))
147+ }
148+ }
149+
150+ packageStartupMessage(
151+ sprintf(
152+ " Failed to configure PROJ (tried %s):\n %s" ,
153+ paste0(" '" , presets , " '" , collapse = " , " ),
154+ paste0(errors , collapse = " \n " )
155+ )
156+ )
157+
158+ return (invisible (NULL ))
159+ },
160+ stop(sprintf(" Unknown preset: '%s'" , preset ))
161+ )
162+ }
163+
164+ # We could check a shared library with dyn.load(), but this may error for
165+ # valid system PROJ that isn't an absolute filename.
166+
167+ if (! is.null(database_path )) {
168+ if (! file.exists(database_path )) {
169+ stop(sprintf(" Invalid database path: '%s' does not exist" , database_path ))
170+ }
171+ }
172+
173+ if (! is.null(search_path )) {
174+ if (! dir.exists(search_path )) {
175+ stop(sprintf(" Invalid search path: '%s' does not exist" , search_path ))
176+ }
177+ }
178+
179+ configure_proj_shared(
180+ shared_library_path = shared_library ,
181+ database_path = database_path ,
182+ search_path = search_path
183+ )
184+ }
185+
186+ configure_proj_system <- function () {
187+ sd_configure_proj(shared_library = proj_dll_name())
188+ }
189+
190+ configure_proj_prefix <- function (prefix ) {
191+ if (! dir.exists(prefix )) {
192+ stop(sprintf(" Can't configure PROJ from prefix '%s': does not exist" , prefix ))
193+ }
194+
195+ sd_configure_proj(
196+ shared_library = file.path(prefix , " lib" , proj_dll_name()),
197+ database_path = file.path(prefix , " share" , " proj" , " proj.db" ),
198+ search_path = file.path(prefix , " share" , " proj" )
199+ )
200+ }
201+
202+ proj_dll_name <- function () {
203+ switch (tolower(Sys.info()[[" sysname" ]]),
204+ windows = " proj.dll" ,
205+ darwin = " libproj.dylib" ,
206+ linux = " libproj.so" ,
207+ stop(sprintf(" Can't determine system PROJ shared library name for OS: %s" , Sys.info()[[" sysname" ]]))
208+ )
209+ }
0 commit comments