aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/Makefile.am16
-rw-r--r--src/apps/projsync.cpp555
-rw-r--r--src/bin_projsync.cmake16
-rw-r--r--src/filemanager.cpp11
-rw-r--r--src/filemanager.hpp12
-rw-r--r--src/networkfilemanager.cpp12
-rw-r--r--src/proj_internal.h3
8 files changed, 622 insertions, 14 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 8cf57306..534bc311 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -15,6 +15,8 @@ option(BUILD_PROJ
"Build proj (cartographic projection tool)" ON)
option(BUILD_PROJINFO
"Build projinfo (SRS and coordinate operation metadata/query tool)" ON)
+option(BUILD_PROJSYNC
+ "Build projsync (synchronize transformation support data)" ON)
if(NOT MSVC)
@@ -72,6 +74,15 @@ if(BUILD_GIE)
set(BIN_TARGETS ${BIN_TARGETS} gie)
endif()
+if(BUILD_PROJSYNC)
+ if(NOT ENABLE_CURL)
+ message(SEND_ERROR "projsync requires Curl")
+ endif()
+ include(bin_projsync.cmake)
+ set(BIN_TARGETS ${BIN_TARGETS} bin_projsync)
+endif()
+
+
if(MSVC OR CMAKE_CONFIGURATION_TYPES)
if(BIN_TARGETS)
# Add _d suffix for your debug versions of the tools
diff --git a/src/Makefile.am b/src/Makefile.am
index de35a754..291bc21e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,5 @@
AM_CFLAGS = @C_WFLAGS@
-bin_PROGRAMS = proj geod cs2cs gie cct projinfo
EXTRA_PROGRAMS = multistresstest test228
TESTS = geodtest
@@ -15,8 +14,11 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesi
EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \
bin_geod.cmake bin_proj.cmake bin_projinfo.cmake \
- lib_proj.cmake CMakeLists.txt bin_geodtest.cmake tests/geodtest.cpp \
- wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h
+ lib_proj.cmake CMakeLists.txt bin_geodtest.cmake \
+ bin_projsync.cmake \
+ tests/geodtest.cpp \
+ wkt1_grammar.y wkt2_grammar.y apps/emess.h apps/utils.h \
+ apps/projsync.cpp
proj_SOURCES = apps/proj.cpp apps/emess.cpp apps/utils.cpp
projinfo_SOURCES = apps/projinfo.cpp
@@ -24,6 +26,14 @@ cs2cs_SOURCES = apps/cs2cs.cpp apps/emess.cpp apps/utils.cpp
cct_SOURCES = apps/cct.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h
geod_SOURCES = apps/geod.cpp apps/geod_set.cpp apps/geod_interface.cpp apps/geod_interface.h apps/emess.cpp
+if HAVE_CURL
+projsync_SOURCES = apps/projsync.cpp
+projsync_LDADD = libproj.la
+PROJSYNC_BIN = projsync
+endif
+
+bin_PROGRAMS = proj geod cs2cs gie cct projinfo $(PROJSYNC_BIN)
+
gie_SOURCES = apps/gie.cpp apps/proj_strtod.cpp apps/proj_strtod.h apps/optargpm.h
multistresstest_SOURCES = tests/multistresstest.cpp
test228_SOURCES = tests/test228.cpp
diff --git a/src/apps/projsync.cpp b/src/apps/projsync.cpp
new file mode 100644
index 00000000..3fab38e1
--- /dev/null
+++ b/src/apps/projsync.cpp
@@ -0,0 +1,555 @@
+/******************************************************************************
+ * Project: PROJ
+ * Purpose: Downloader tool
+ * Author: Even Rouault, <even.rouault at spatialys.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2020, Even Rouault, <even.rouault at spatialys.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+//! @cond Doxygen_Suppress
+
+#define FROM_PROJ_CPP
+
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <set>
+#include <string>
+
+#include "filemanager.hpp"
+#include "proj.h"
+#include "proj_internal.h"
+
+#include "proj/internal/include_nlohmann_json.hpp"
+#include "proj/internal/internal.hpp"
+
+using json = nlohmann::json;
+using namespace NS_PROJ::internal;
+
+// ---------------------------------------------------------------------------
+
+namespace {
+class ParsingException : public std::exception {
+ std::string msg_;
+
+ public:
+ ParsingException(const char *msg) : msg_(msg) {}
+ const char *what() const noexcept override { return msg_.c_str(); }
+};
+}
+
+// ---------------------------------------------------------------------------
+
+static void usage() {
+ std::cerr << "usage: projsync " << std::endl;
+ std::cerr << " [--endpoint URL]" << std::endl;
+ std::cerr << " [--local-geojson-file FILENAME]" << std::endl;
+ std::cerr << " ([--user-writable-directory] | "
+ "[--system-directory] | [--target-dir DIRNAME])"
+ << std::endl;
+ std::cerr << " [--bbox west_long,south_lat,east_long,north_lat]"
+ << std::endl;
+ std::cerr << " [--spatial-test contains|intersects]" << std::endl;
+ std::cerr << " [--source-id ID] [--area-of-use NAME]" << std::endl;
+ std::cerr << " [--file NAME]" << std::endl;
+ std::cerr << " [--all] [--exclude-world-coverage]" << std::endl;
+ std::cerr << " [--quiet] [--dry-run] [--list-files]" << std::endl;
+ std::exit(1);
+}
+
+// ---------------------------------------------------------------------------
+
+static std::vector<double> get_bbox(const json &j) {
+ std::vector<double> res;
+ if (j.size() == 2 && j[0].is_number() && j[1].is_number()) {
+ res.push_back(j[0].get<double>());
+ res.push_back(j[1].get<double>());
+ res.push_back(j[0].get<double>());
+ res.push_back(j[1].get<double>());
+ } else {
+ for (const auto &obj : j) {
+ if (obj.is_array()) {
+ const auto subres = get_bbox(obj);
+ if (subres.size() == 4) {
+ if (res.empty()) {
+ res = subres;
+ } else {
+ res[0] = std::min(res[0], subres[0]);
+ res[1] = std::min(res[1], subres[1]);
+ res[2] = std::max(res[2], subres[2]);
+ res[3] = std::max(res[3], subres[3]);
+ }
+ }
+ }
+ }
+ }
+ return res;
+}
+
+// ---------------------------------------------------------------------------
+
+int main(int argc, char *argv[]) {
+ auto ctx = pj_get_default_ctx();
+
+ std::string targetDir;
+ std::string endpoint(pj_context_get_url_endpoint(ctx));
+ const std::string geojsonFile("files.geojson");
+ std::string queriedSourceId;
+ std::string queriedAreaOfUse;
+ bool listFiles = false;
+ bool dryRun = false;
+ bool hasQueriedBbox = false;
+ double queried_west = 0.0;
+ double queried_south = 0.0;
+ double queried_east = 0.0;
+ double queried_north = 0.0;
+ bool intersects = true;
+ bool quiet = false;
+ bool includeWorldCoverage = true;
+ bool queryAll = false;
+ std::string queriedFilename;
+ std::string files_geojson_local;
+
+ for (int i = 1; i < argc; i++) {
+ std::string arg(argv[i]);
+ if (arg == "--endpoint" && i + 1 < argc) {
+ i++;
+ endpoint = argv[i];
+ } else if (arg == "--user-writable-directory") {
+ // do nothing
+ } else if (arg == "--system-directory") {
+ targetDir = PROJ_LIB;
+ } else if (arg == "--target-dir" && i + 1 < argc) {
+ i++;
+ targetDir = argv[i];
+ } else if (arg == "--local-geojson-file" && i + 1 < argc) {
+ i++;
+ files_geojson_local = argv[i];
+ } else if (arg == "--list-files") {
+ listFiles = true;
+ } else if (arg == "--source-id" && i + 1 < argc) {
+ i++;
+ queriedSourceId = argv[i];
+ } else if (arg == "--area-of-use" && i + 1 < argc) {
+ i++;
+ queriedAreaOfUse = argv[i];
+ } else if (arg == "--file" && i + 1 < argc) {
+ i++;
+ queriedFilename = argv[i];
+ } else if (arg == "--bbox" && i + 1 < argc) {
+ i++;
+ auto bboxStr(argv[i]);
+ auto bbox(split(bboxStr, ','));
+ if (bbox.size() != 4) {
+ std::cerr << "Incorrect number of values for option --bbox: "
+ << bboxStr << std::endl;
+ usage();
+ }
+ try {
+ queried_west = c_locale_stod(bbox[0]);
+ queried_south = c_locale_stod(bbox[1]);
+ queried_east = c_locale_stod(bbox[2]);
+ queried_north = c_locale_stod(bbox[3]);
+ } catch (const std::exception &e) {
+ std::cerr << "Invalid value for option --bbox: " << bboxStr
+ << ", " << e.what() << std::endl;
+ usage();
+ }
+ if (queried_west > 180 && queried_east > queried_west) {
+ queried_west -= 360;
+ queried_east -= 360;
+ } else if (queried_west < -180 && queried_east > queried_west) {
+ queried_west += 360;
+ queried_east += 360;
+ } else if (fabs(queried_west) < 180 && fabs(queried_east) < 180 &&
+ queried_east < queried_west) {
+ queried_east += 360;
+ }
+ hasQueriedBbox = true;
+ } else if (arg == "--spatial-test" && i + 1 < argc) {
+ i++;
+ const std::string value(argv[i]);
+ if (ci_equal(value, "contains")) {
+ intersects = false;
+ } else if (ci_equal(value, "intersects")) {
+ intersects = true;
+ } else {
+ std::cerr << "Unrecognized value for option --spatial-test: "
+ << value << std::endl;
+ usage();
+ }
+ } else if (arg == "--dry-run") {
+ dryRun = true;
+ } else if (arg == "--exclude-world-coverage") {
+ includeWorldCoverage = false;
+ } else if (arg == "--all") {
+ queryAll = true;
+ } else if (arg == "-q" || arg == "--quiet") {
+ quiet = true;
+ } else {
+ usage();
+ }
+ }
+ if (!listFiles && queriedFilename.empty() && queriedSourceId.empty() &&
+ queriedAreaOfUse.empty() && !hasQueriedBbox && !queryAll) {
+ std::cerr << "At least one of --list-files, --file, --source-id, "
+ "--area-of-use, --bbox or --all must be specified."
+ << std::endl
+ << std::endl;
+ usage();
+ }
+
+ if (targetDir.empty()) {
+ targetDir = pj_context_get_user_writable_directory(ctx, true);
+ } else {
+ if (targetDir.back() == '/') {
+ targetDir.resize(targetDir.size() - 1);
+ }
+
+ // This is used by projsync() to determine where to write files.
+ pj_context_set_user_writable_directory(ctx, targetDir);
+ }
+
+ if (!endpoint.empty() && endpoint.back() == '/') {
+ endpoint.resize(endpoint.size() - 1);
+ }
+
+ if (!quiet) {
+ std::cout << "Downloading from " << endpoint << " into " << targetDir
+ << std::endl;
+ }
+
+ proj_context_set_enable_network(ctx, true);
+ if (files_geojson_local.empty()) {
+ const std::string files_geojson_url(endpoint + '/' + geojsonFile);
+ if (!proj_download_file(ctx, files_geojson_url.c_str(), false, nullptr,
+ nullptr)) {
+ std::cerr << "Cannot download " << geojsonFile << std::endl;
+ std::exit(1);
+ }
+
+ files_geojson_local = targetDir + '/' + geojsonFile;
+ }
+
+ auto file = NS_PROJ::FileManager::open(ctx, files_geojson_local.c_str(),
+ NS_PROJ::FileAccess::READ_ONLY);
+ if (!file) {
+ std::cerr << "Cannot open " << files_geojson_local << std::endl;
+ std::exit(1);
+ }
+
+ std::string text;
+ while (true) {
+ bool maxLenReached = false;
+ bool eofReached = false;
+ text += file->read_line(1000000, maxLenReached, eofReached);
+ if (maxLenReached) {
+ std::cerr << "Error while parsing " << geojsonFile
+ << " : too long line" << std::endl;
+ std::exit(1);
+ }
+ if (eofReached)
+ break;
+ }
+ file.reset();
+
+ if (listFiles) {
+ std::cout << "filename,source_id,area_of_use,file_size" << std::endl;
+ }
+
+ try {
+ const auto j = json::parse(text);
+ bool foundMatchSourceIdCriterion = false;
+ std::set<std::string> source_ids;
+ bool foundMatchAreaOfUseCriterion = false;
+ std::set<std::string> areas_of_use;
+ bool foundMatchFileCriterion = false;
+ std::set<std::string> files;
+ if (!j.is_object() || !j.contains("features")) {
+ throw ParsingException("no features member");
+ }
+ std::vector<std::string> to_download;
+ unsigned long long total_size_to_download = 0;
+ for (const auto feat : j["features"]) {
+ if (!feat.is_object()) {
+ continue;
+ }
+ if (!feat.contains("properties")) {
+ continue;
+ }
+ const auto properties = feat["properties"];
+ if (!properties.is_object()) {
+ continue;
+ }
+
+ if (!properties.contains("name")) {
+ continue;
+ }
+ const auto j_name = properties["name"];
+ if (!j_name.is_string()) {
+ continue;
+ }
+ const auto name(j_name.get<std::string>());
+ files.insert(name);
+
+ if (!properties.contains("source_id")) {
+ continue;
+ }
+ const auto j_source_id = properties["source_id"];
+ if (!j_source_id.is_string()) {
+ continue;
+ }
+ const auto source_id(j_source_id.get<std::string>());
+ source_ids.insert(source_id);
+
+ std::string area_of_use;
+ if (properties.contains("area_of_use")) {
+ const auto j_area_of_use = properties["area_of_use"];
+ if (j_area_of_use.is_string()) {
+ area_of_use = j_area_of_use.get<std::string>();
+ areas_of_use.insert(area_of_use);
+ }
+ }
+
+ unsigned long long file_size = 0;
+ if (properties.contains("file_size")) {
+ const auto j_file_size = properties["file_size"];
+ if (j_file_size.type() == json::value_t::number_unsigned) {
+ file_size = j_file_size.get<unsigned long long>();
+ }
+ }
+
+ if (listFiles) {
+ std::cout << name << "," << area_of_use << "," << source_id
+ << "," << file_size << std::endl;
+ continue;
+ }
+
+ const bool matchSourceId =
+ queryAll || queriedSourceId.empty() ||
+ source_id.find(queriedSourceId) != std::string::npos;
+ if (!queriedSourceId.empty() &&
+ source_id.find(queriedSourceId) != std::string::npos) {
+ foundMatchSourceIdCriterion = true;
+ }
+
+ const bool matchAreaOfUse =
+ queryAll || queriedAreaOfUse.empty() ||
+ area_of_use.find(queriedAreaOfUse) != std::string::npos;
+ if (!queriedAreaOfUse.empty() &&
+ area_of_use.find(queriedAreaOfUse) != std::string::npos) {
+ foundMatchAreaOfUseCriterion = true;
+ }
+
+ const bool matchFile =
+ queryAll || queriedFilename.empty() ||
+ name.find(queriedFilename) != std::string::npos;
+ if (!queriedFilename.empty() &&
+ name.find(queriedFilename) != std::string::npos) {
+ foundMatchFileCriterion = true;
+ }
+
+ bool matchBbox = true;
+ if (queryAll || hasQueriedBbox) {
+ matchBbox = false;
+ do {
+ if (!feat.contains("geometry")) {
+ if (queryAll) {
+ matchBbox = true;
+ }
+ break;
+ }
+ const auto j_geometry = feat["geometry"];
+ if (!j_geometry.is_object()) {
+ if (queryAll) {
+ matchBbox = true;
+ }
+ break;
+ }
+ if (!j_geometry.contains("coordinates")) {
+ break;
+ }
+ const auto j_coordinates = j_geometry["coordinates"];
+ if (!j_coordinates.is_array()) {
+ break;
+ }
+ if (!j_geometry.contains("type")) {
+ break;
+ }
+ const auto j_geometry_type = j_geometry["type"];
+ if (!j_geometry_type.is_string()) {
+ break;
+ }
+ const auto geometry_type(
+ j_geometry_type.get<std::string>());
+ std::vector<double> grid_bbox;
+ if (geometry_type == "MultiPolygon") {
+ std::vector<std::vector<double>> grid_bboxes;
+ bool foundMinus180 = false;
+ bool foundPlus180 = false;
+ for (const auto obj : j_coordinates) {
+ if (obj.is_array()) {
+ const auto tmp = get_bbox(obj);
+ if (tmp.size() == 4) {
+ if (tmp[0] == -180)
+ foundMinus180 = true;
+ else if (tmp[2] == 180)
+ foundPlus180 = true;
+ grid_bboxes.push_back(tmp);
+ }
+ }
+ }
+ for (auto &bbox : grid_bboxes) {
+ if (foundMinus180 && foundPlus180 &&
+ bbox[0] == -180) {
+ bbox[0] = 180;
+ bbox[2] += 360;
+ }
+ if (grid_bbox.empty()) {
+ grid_bbox = bbox;
+ } else {
+ grid_bbox[0] = std::min(grid_bbox[0], bbox[0]);
+ grid_bbox[1] = std::min(grid_bbox[1], bbox[1]);
+ grid_bbox[2] = std::max(grid_bbox[2], bbox[2]);
+ grid_bbox[3] = std::max(grid_bbox[3], bbox[3]);
+ }
+ }
+ } else {
+ grid_bbox = get_bbox(j_coordinates);
+ }
+ if (grid_bbox.size() != 4) {
+ break;
+ }
+ double grid_w = grid_bbox[0];
+ const double grid_s = grid_bbox[1];
+ double grid_e = grid_bbox[2];
+ const double grid_n = grid_bbox[3];
+ if (grid_e - grid_w > 359 && grid_n - grid_s > 179) {
+ if (!includeWorldCoverage) {
+ break;
+ }
+ grid_w = -std::numeric_limits<double>::max();
+ grid_e = std::numeric_limits<double>::max();
+ } else if (grid_e > 180 && queried_west < -180) {
+ grid_w -= 360;
+ grid_e -= 360;
+ }
+ if (queryAll) {
+ matchBbox = true;
+ break;
+ }
+
+ if (intersects) {
+ if (queried_west < grid_e && grid_w < queried_east &&
+ queried_south < grid_n && grid_s < queried_north) {
+ matchBbox = true;
+ }
+ } else {
+ if (grid_w >= queried_west && grid_s >= queried_south &&
+ grid_e <= queried_east && grid_n <= queried_north) {
+ matchBbox = true;
+ }
+ }
+
+ } while (false);
+ }
+
+ if (matchFile & matchSourceId && matchAreaOfUse && matchBbox) {
+ const std::string resource_url(endpoint + '/' + name);
+ if (proj_is_download_needed(ctx, resource_url.c_str(), false)) {
+ total_size_to_download += file_size;
+ to_download.push_back(resource_url);
+ } else {
+ if (!quiet) {
+ std::cout << resource_url << " already downloaded."
+ << std::endl;
+ }
+ }
+ }
+ }
+
+ if (!quiet && total_size_to_download > 0) {
+ if (total_size_to_download > 1024 * 1024)
+ std::cout << "Total size to download: "
+ << total_size_to_download / (1024 * 1024) << " MB"
+ << std::endl;
+ else
+ std::cout << "Total to download: " << total_size_to_download
+ << " bytes" << std::endl;
+ }
+ for (size_t i = 0; i < to_download.size(); ++i) {
+ const auto &url = to_download[i];
+ if (!quiet) {
+ if (dryRun) {
+ std::cout << "Would download ";
+ } else {
+ std::cout << "Downloading ";
+ }
+ std::cout << url << "... (" << i + 1 << " / "
+ << to_download.size() << ")" << std::endl;
+ }
+ if (!dryRun &&
+ !proj_download_file(ctx, url.c_str(), false, nullptr,
+ nullptr)) {
+ std::cerr << "Cannot download " << url << std::endl;
+ std::exit(1);
+ }
+ }
+
+ if (!queriedSourceId.empty() && !foundMatchSourceIdCriterion) {
+ std::cerr << "Warning: '" << queriedSourceId
+ << "' is a unknown value for --source-id." << std::endl;
+ std::cerr << "Known values are:" << std::endl;
+ for (const auto &v : source_ids) {
+ std::cerr << " " << v << std::endl;
+ }
+ std::exit(1);
+ }
+
+ if (!queriedAreaOfUse.empty() && !foundMatchAreaOfUseCriterion) {
+ std::cerr << "Warning: '" << queriedAreaOfUse
+ << "' is a unknown value for --area-of-use." << std::endl;
+ std::cerr << "Known values are:" << std::endl;
+ for (const auto &v : areas_of_use) {
+ std::cerr << " " << v << std::endl;
+ }
+ std::exit(1);
+ }
+
+ if (!queriedFilename.empty() && !foundMatchFileCriterion) {
+ std::cerr << "Warning: '" << queriedFilename
+ << "' is a unknown value for --file." << std::endl;
+ std::cerr << "Known values are:" << std::endl;
+ for (const auto &v : files) {
+ std::cerr << " " << v << std::endl;
+ }
+ std::exit(1);
+ }
+ } catch (const std::exception &e) {
+ std::cerr << "Error: " << e.what() << std::endl;
+ std::exit(1);
+ }
+
+ return 0;
+}
+
+//! @endcond
diff --git a/src/bin_projsync.cmake b/src/bin_projsync.cmake
new file mode 100644
index 00000000..07891730
--- /dev/null
+++ b/src/bin_projsync.cmake
@@ -0,0 +1,16 @@
+set(PROJSYNC_SRC apps/projsync.cpp)
+
+source_group("Source Files\\Bin" FILES ${PROJSYNC_SRC})
+
+add_executable(bin_projsync ${PROJSYNC_SRC})
+set_target_properties(bin_projsync
+ PROPERTIES
+ OUTPUT_NAME projsync)
+target_link_libraries(bin_projsync ${PROJ_LIBRARIES})
+target_compile_options(bin_projsync PRIVATE ${PROJ_CXX_WARN_FLAGS})
+install(TARGETS bin_projsync
+ RUNTIME DESTINATION ${BINDIR})
+
+if(MSVC AND BUILD_LIBPROJ_SHARED)
+ target_compile_definitions(bin_projsync PRIVATE PROJ_MSVC_DLL_IMPORT=1)
+endif()
diff --git a/src/filemanager.cpp b/src/filemanager.cpp
index 17e09b21..aea9b11d 100644
--- a/src/filemanager.cpp
+++ b/src/filemanager.cpp
@@ -1178,6 +1178,8 @@ static void CreateDirectoryRecursively(PJ_CONTEXT *ctx,
std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx,
bool create) {
+ if (!ctx)
+ ctx = pj_get_default_ctx();
if (ctx->user_writable_directory.empty()) {
// For testing purposes only
const char *env_var_PROJ_USER_WRITABLE_DIRECTORY =
@@ -1234,6 +1236,15 @@ std::string pj_context_get_user_writable_directory(PJ_CONTEXT *ctx,
// ---------------------------------------------------------------------------
+void pj_context_set_user_writable_directory(PJ_CONTEXT *ctx,
+ const std::string &path) {
+ if (!ctx)
+ ctx = pj_get_default_ctx();
+ ctx->user_writable_directory = path;
+}
+
+// ---------------------------------------------------------------------------
+
#ifdef WIN32
static const char dir_chars[] = "/\\";
#else
diff --git a/src/filemanager.hpp b/src/filemanager.hpp
index bbd12b7e..9446a0bc 100644
--- a/src/filemanager.hpp
+++ b/src/filemanager.hpp
@@ -53,8 +53,8 @@ class FileManager {
public:
// "Low-level" interface.
- static std::unique_ptr<File> open(PJ_CONTEXT *ctx, const char *filename,
- FileAccess access);
+ static PROJ_DLL std::unique_ptr<File>
+ open(PJ_CONTEXT *ctx, const char *filename, FileAccess access);
static bool exists(PJ_CONTEXT *ctx, const char *filename);
static bool mkdir(PJ_CONTEXT *ctx, const char *filename);
static bool unlink(PJ_CONTEXT *ctx, const char *filename);
@@ -81,14 +81,15 @@ class File {
explicit File(const std::string &name);
public:
- virtual ~File();
+ virtual PROJ_DLL ~File();
virtual size_t read(void *buffer, size_t sizeBytes) = 0;
virtual size_t write(const void *buffer, size_t sizeBytes) = 0;
virtual bool seek(unsigned long long offset, int whence = SEEK_SET) = 0;
virtual unsigned long long tell() = 0;
virtual void reassign_context(PJ_CONTEXT *ctx) = 0;
virtual bool hasChanged() const = 0;
- std::string read_line(size_t maxLen, bool &maxLenReached, bool &eofReached);
+ std::string PROJ_DLL read_line(size_t maxLen, bool &maxLenReached,
+ bool &eofReached);
const std::string &name() const { return name_; }
};
@@ -99,7 +100,8 @@ std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT *ctx,
const char *filename);
NS_PROJ_END
-std::vector<std::string> pj_get_default_searchpaths(PJ_CONTEXT *ctx);
+// Exported for projsync
+std::vector<std::string> PROJ_DLL pj_get_default_searchpaths(PJ_CONTEXT *ctx);
//! @endcond Doxygen_Suppress
diff --git a/src/networkfilemanager.cpp b/src/networkfilemanager.cpp
index c4cb41d9..d252fd84 100644
--- a/src/networkfilemanager.cpp
+++ b/src/networkfilemanager.cpp
@@ -193,7 +193,7 @@ class DiskChunkCache {
std::unique_ptr<SQLite3VFS> vfs_{};
explicit DiskChunkCache(PJ_CONTEXT *ctx, const std::string &path);
-
+
bool initialize();
void commitAndClose();
@@ -552,7 +552,8 @@ bool DiskChunkCache::checkConsistency() {
void DiskChunkCache::commitAndClose() {
if (hDB_) {
- if( sqlite3_exec(hDB_, "COMMIT", nullptr, nullptr, nullptr) != SQLITE_OK ) {
+ if (sqlite3_exec(hDB_, "COMMIT", nullptr, nullptr, nullptr) !=
+ SQLITE_OK) {
pj_log(ctx_, PJ_LOG_ERROR, "%s", sqlite3_errmsg(hDB_));
}
sqlite3_close(hDB_);
@@ -562,9 +563,7 @@ void DiskChunkCache::commitAndClose() {
// ---------------------------------------------------------------------------
-DiskChunkCache::~DiskChunkCache() {
- commitAndClose();
-}
+DiskChunkCache::~DiskChunkCache() { commitAndClose(); }
// ---------------------------------------------------------------------------
@@ -1336,7 +1335,8 @@ std::unique_ptr<File> NetworkFile::open(PJ_CONTEXT *ctx, const char *filename) {
// ---------------------------------------------------------------------------
-std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT* ctx, const char* filename) {
+std::unique_ptr<File> pj_network_file_open(PJ_CONTEXT *ctx,
+ const char *filename) {
return NetworkFile::open(ctx, filename);
}
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 69d583fc..7a777027 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -870,7 +870,10 @@ void pj_load_ini(PJ_CONTEXT* ctx);
// Exported for testing purposes only
std::string PROJ_DLL pj_context_get_grid_cache_filename(PJ_CONTEXT *ctx);
+
+// For use by projsync
std::string PROJ_DLL pj_context_get_user_writable_directory(PJ_CONTEXT *ctx, bool create);
+void PROJ_DLL pj_context_set_user_writable_directory(PJ_CONTEXT* ctx, const std::string& path);
/* classic public API */
#include "proj_api.h"