aboutsummaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorEven Rouault <even.rouault@spatialys.com>2020-01-22 18:31:26 +0100
committerGitHub <noreply@github.com>2020-01-22 18:31:26 +0100
commitdb31b6dfa9c8fe37d5706d95ce81012b8db3c3b9 (patch)
treedc592c2b56f8af476c42a51f5dbc6ee04fabc280 /test
parent1ad703a58ce1867fe2ede96ebced1bdec9c63d65 (diff)
downloadPROJ-db31b6dfa9c8fe37d5706d95ce81012b8db3c3b9.tar.gz
PROJ-db31b6dfa9c8fe37d5706d95ce81012b8db3c3b9.zip
Merge RFC4 (#1865)
This commit is the result of the squashing of rfc4_dev branch in a single commit. It implements mostly RFC 4 related work. * Grid handling: - remove obsolete and presumably unfinished implementation of grid catalog functionality - all grid functionality is in grids.cpp/.hpp - vertical and horizontal grid shift: rework to no longer load whole grid into memory - remove hgrids and vgrids member from PJ structure, and store them in hgridshift/vgridshift/deformation structures - build systems: add optional libtiff dependency. Must be explicitly disabled if not desired - add support for horizontal and vertical grids in GeoTIFF, if libtiff is available - add GenericShiftGridSet and GenericShiftGrid classes, relying on TIFF grids, that can be used for generic purpose grid-based adjustment - add a +proj=xyzgridshift method to perform geocentric translation by grid. Used for French NTF to RGF93 transformation using gr3df97a.tif grid - deformation: add support for +grids= for GeoTIFF grids - horizontal grid shift: fix failures on points slightly outside a subgrid (fixes #209) * File management: - add a filemanager.cpp/.hpp to deal with file related work - test for legacy proj_api.h fileapi - proj.h: add proj_context_set_fileapi() and proj_context_set_sqlite3_vfs_name() (fixes #866) - add capability to read resource files from the user writable directory * Network access: - build systems: add optional curl dependency - add a curl-based default implementation for network related functionality - proj.h: add C API to control network functionality, and optionaly provide network callbacks - add data/proj.ini with default settings - add a SQLite3 local cache of downloaded chunks - add proj_is_download_needed() and proj_download_file() * Use Win32 Unicode APIs and expect all strings to be UTF-8 (fixes #1765) For backward compatibility, if PROJ_LIB content is found to be not UTF-8 or pointing to a non existing directory, then an attempt at interpretating it in the ANSI page encoding is done. proj_context_set_search_paths() now assumes strings to be in UTF-8, and functions returning paths will also return values in UTF-8.
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt1
-rw-r--r--test/cli/Makefile.am12
-rw-r--r--test/cli/ntv2_out.dist8
-rw-r--r--test/cli/td_out.dist2
-rwxr-xr-xtest/cli/test272
-rwxr-xr-xtest/cli/test832
-rwxr-xr-xtest/cli/testdatumfile6
-rwxr-xr-xtest/cli/testntv213
-rw-r--r--test/gie/4D-API_cs2cs-style.gie14
-rw-r--r--test/gie/Makefile.am26
-rw-r--r--test/gie/deformation.gie29
-rw-r--r--test/gie/geotiff_grids.gie324
-rw-r--r--test/gigs/Makefile.am34
-rw-r--r--test/unit/CMakeLists.txt33
-rw-r--r--test/unit/Makefile.am20
-rw-r--r--test/unit/gie_self_tests.cpp5
-rw-r--r--test/unit/pj_transform_test.cpp89
-rw-r--r--test/unit/proj_context_test.cpp63
-rw-r--r--test/unit/test_c_api.cpp21
-rw-r--r--test/unit/test_factory.cpp58
-rw-r--r--test/unit/test_grids.cpp227
-rw-r--r--test/unit/test_network.cpp1856
-rw-r--r--test/unit/test_operation.cpp18
23 files changed, 2759 insertions, 104 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ae721d46..a7aac755 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -10,6 +10,7 @@ proj_add_gie_test("GDA" "gie/GDA.gie")
proj_add_gie_test("4D-API-cs2cs-style" "gie/4D-API_cs2cs-style.gie")
proj_add_gie_test("DHDN_ETRS89" "gie/DHDN_ETRS89.gie")
proj_add_gie_test("Unitconvert" "gie/unitconvert.gie")
+proj_add_gie_test("geotiff_grids" "gie/geotiff_grids.gie")
# GIGS tests. Uncommented tests are expected to fail due to issues with
# various projections. Should be investigated further and fixed.
diff --git a/test/cli/Makefile.am b/test/cli/Makefile.am
index 33878743..417ec137 100644
--- a/test/cli/Makefile.am
+++ b/test/cli/Makefile.am
@@ -29,7 +29,7 @@ EXTRA_DIST = pj_out27.dist pj_out83.dist td_out.dist \
CMakeLists.txt
testprojinfo-check:
- PROJ_LIB=$(PROJ_LIB) $(TESTPROJINFO) $(PROJINFOEXE)
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTPROJINFO) $(PROJINFOEXE)
test27-check:
$(TEST27) $(PROJEXE)
@@ -41,24 +41,24 @@ testproj-check:
$(TESTPROJ) $(PROJEXE)
testvarious-check:
- PROJ_LIB=$(PROJ_LIB) $(TESTVARIOUS) $(CS2CSEXE)
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTVARIOUS) $(CS2CSEXE)
testdatumfile-check:
@if [ -f $(PROJ_LIB)/conus -a -f $(PROJ_LIB)/ntv1_can.dat -a -f $(PROJ_LIB)/MD -a -f $(PROJ_LIB)/ntf_r93.gsb -a -f $(PROJ_LIB)/egm96_15.gtx ]; then \
- PROJ_LIB=$(PROJ_LIB) $(TESTDATUMFILE) $(CS2CSEXE) ; \
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTDATUMFILE) $(CS2CSEXE) ; \
fi
testign-check:
@if [ -f $(PROJ_LIB)/ntf_r93.gsb ] ; then \
- PROJ_LIB=$(PROJ_LIB) $(TESTIGN) $(CS2CSEXE) ; \
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTIGN) $(CS2CSEXE) ; \
fi
testntv2-check:
@if [ -f $(PROJ_LIB)/ntv2_0.gsb -a -f $(PROJ_LIB)/conus -a -f $(PROJ_LIB)/ntv1_can.dat ] ; then \
- PROJ_LIB=$(PROJ_LIB) $(TESTNTV2) $(CS2CSEXE) ; \
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTNTV2) $(CS2CSEXE) ; \
fi
testcct-check:
- PROJ_LIB=$(PROJ_LIB) $(TESTCCT) $(CCTEXE)
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(TESTCCT) $(CCTEXE)
check-local: testprojinfo-check test27-check test83-check testproj-check testvarious-check testdatumfile-check testign-check testntv2-check testcct-check
diff --git a/test/cli/ntv2_out.dist b/test/cli/ntv2_out.dist
index 531db7be..d90fdc37 100644
--- a/test/cli/ntv2_out.dist
+++ b/test/cli/ntv2_out.dist
@@ -13,6 +13,14 @@ Try with NTv2 and NTv1 together ... falls back to NTv1
Switching between NTv2 subgrids
-112.5839956 49.4914451 0 -112.58307487 49.49145197 0.00000000
##############################################################
+Interpolating very close (and sometimes a bit outside) to the edges a NTv2 subgrid (#209)
+-115.58333333 51.25000000 0 -115.58228512 51.24997866 0.00000000
+-115.58333333 51.25000010 0 -115.58228512 51.24997876 0.00000000
+-115.58333334 51.25000000 0 -115.58228513 51.24997866 0.00000000
+-115.49166667 51.07500000 0 -115.49062909 51.07497666 0.00000000
+-115.49166668 51.07500000 0 -115.49062910 51.07497666 0.00000000
+-115.49166667 51.07499990 0 -115.49062909 51.07497656 0.00000000
+##############################################################
Attempt first with ntv2_0.gsb and then conus
-111.5 45.26 -111.50079772 45.25992835 0.00000000
##############################################################
diff --git a/test/cli/td_out.dist b/test/cli/td_out.dist
index 478a0d84..cf4b8d73 100644
--- a/test/cli/td_out.dist
+++ b/test/cli/td_out.dist
@@ -21,7 +21,7 @@ edge or even a wee bit outside (#141).
-5.5001 52.0 -5.500100000000 52.000000000000 0.000000000000
-5.5 52.0 -5.498893534472 52.000109529716 0.000000000000
-5.5000000000001 52.0000000000001 -5.498893534472 52.000109529717 0.000000000000
--5.4999 51.9999 -5.498793541695 52.000009529743 0.000000000000
+-5.4999 51.9999 -5.498793593803 52.000009531513 0.000000000000
-5.5001 52.0 -5.500100000000 52.000000000000 0.000000000000
##############################################################
NAD27 -> NAD83: 1st through ntv1 or ntv2, 2nd through conus
diff --git a/test/cli/test27 b/test/cli/test27
index bfc1cb0a..5825ed80 100755
--- a/test/cli/test27
+++ b/test/cli/test27
@@ -34,7 +34,7 @@ echo "Running ${0} using ${EXE}:"
echo "============================================"
OUT=proj_out27
-INIT_FILE=${PROJ_LIB}/nad27
+INIT_FILE=nad27
#
echo "doing tests into file ${OUT}, please wait"
#
diff --git a/test/cli/test83 b/test/cli/test83
index cfb1365e..8c1293d0 100755
--- a/test/cli/test83
+++ b/test/cli/test83
@@ -35,7 +35,7 @@ echo "Running ${0} using ${EXE}:"
echo "============================================"
OUT=proj_out83
-INIT_FILE=${PROJ_LIB}/nad83
+INIT_FILE=nad83
#
echo "doing tests into file ${OUT}, please wait"
#
diff --git a/test/cli/testdatumfile b/test/cli/testdatumfile
index e4b9ea2d..16e4bbc3 100755
--- a/test/cli/testdatumfile
+++ b/test/cli/testdatumfile
@@ -27,7 +27,11 @@ echo "Running ${0} using ${EXE}:"
echo "============================================"
mkdir "dir with \" space"
-cp ${PROJ_LIB}/conus "dir with \" space/myconus"
+if test -f "${PROJ_LIB}/conus"; then
+ cp "${PROJ_LIB}/conus" "dir with \" space/myconus"
+else
+ cp "`dirname $0`/../../data/conus" "dir with \" space/myconus"
+fi
OUT=td_out
#EXE=../src/cs2cs
diff --git a/test/cli/testntv2 b/test/cli/testntv2
index 72a0f9a2..44ccac1e 100755
--- a/test/cli/testntv2
+++ b/test/cli/testntv2
@@ -61,11 +61,22 @@ $EXE +proj=latlong +datum=NAD83 +to +proj=latlong +ellps=clrk66 +nadgrids=ntv2_0
EOF
echo "##############################################################" >> ${OUT}
+echo "Interpolating very close (and sometimes a bit outside) to the edges a NTv2 subgrid (#209)" >> ${OUT}
+$EXE +proj=latlong +datum=NAD83 +to +proj=latlong +ellps=clrk66 +nadgrids=ntv2_0.gsb -E -d 8 >>${OUT} <<EOF
+-115.58333333 51.25000000 0
+-115.58333333 51.25000010 0
+-115.58333334 51.25000000 0
+-115.49166667 51.07500000 0
+-115.49166668 51.07500000 0
+-115.49166667 51.07499990 0
+EOF
+
+echo "##############################################################" >> ${OUT}
echo Attempt first with ntv2_0.gsb and then conus >> ${OUT}
$EXE +proj=longlat +datum=NAD27 +to +proj=longlat +datum=WGS84 -E -d 8 >>${OUT} <<EOF
-111.5 45.26
EOF
-#
+
echo "##############################################################" >> ${OUT}
echo "NAD27 -> NAD83: 1st through ntv2, 2nd through conus" >> ${OUT}
#
diff --git a/test/gie/4D-API_cs2cs-style.gie b/test/gie/4D-API_cs2cs-style.gie
index e5722b5e..3e4b9d2c 100644
--- a/test/gie/4D-API_cs2cs-style.gie
+++ b/test/gie/4D-API_cs2cs-style.gie
@@ -442,12 +442,24 @@ Test bugfix of https://github.com/OSGeo/proj.4/issues/1002
-------------------------------------------------------------------------------
operation +proj=latlong +ellps=WGS84 +geoidgrids=tests/test_nodata.gtx
-------------------------------------------------------------------------------
-ignore pjd_err_failed_to_load_grid
accept 4.05 52.1 0
expect 4.05 52.1 -10
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
+Test null grid with vgridshift
+-------------------------------------------------------------------------------
+operation proj=vgridshift grids=tests/test_nodata.gtx,null ellps=GRS80
+-------------------------------------------------------------------------------
+accept 4.05 52.1 0
+expect 4.05 52.1 -10
+
+# Outside validity area of test_nodata.gtx. Fallback on null
+accept 4.05 -52.1 0
+expect 4.05 -52.1 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
Test bug fix of https://github.com/OSGeo/proj.4/issues/1025.
Using geocent in the new API with a custom ellipsoid should return coordinates
that correspond to that particular ellipsoid and not WGS84 as demonstrated in
diff --git a/test/gie/Makefile.am b/test/gie/Makefile.am
index 058b1fe9..560fea0b 100644
--- a/test/gie/Makefile.am
+++ b/test/gie/Makefile.am
@@ -9,35 +9,39 @@ EXTRA_DIST = 4D-API_cs2cs-style.gie \
ellipsoid.gie \
more_builtins.gie \
unitconvert.gie \
- DHDN_ETRS89.gie
+ DHDN_ETRS89.gie \
+ geotiff_grids.gie
PROJ_LIB ?= ../../data/for_tests
4D-API-cs2cs-style: 4D-API_cs2cs-style.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
GDA: GDA.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
axisswap: axisswap.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
builtins: builtins.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
deformation: deformation.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
ellipsoid: ellipsoid.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
more_builtins: more_builtins.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
unitconvert: unitconvert.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
DHDN_ETRS89: DHDN_ETRS89.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
-check-local: 4D-API-cs2cs-style GDA axisswap builtins deformation ellipsoid more_builtins unitconvert DHDN_ETRS89
+geotiff_grids: geotiff_grids.gie
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+
+check-local: 4D-API-cs2cs-style GDA axisswap builtins deformation ellipsoid more_builtins unitconvert DHDN_ETRS89 geotiff_grids
diff --git a/test/gie/deformation.gie b/test/gie/deformation.gie
index 37f62e6e..848b9e89 100644
--- a/test/gie/deformation.gie
+++ b/test/gie/deformation.gie
@@ -12,6 +12,35 @@ The input coordinate is located at lon=60, lam=-160 - somewhere in Alaska.
<gie>
-------------------------------------------------------------------------------
+Test with an extract of nkgrf03vel_realigned with ctable2+gtx
+-------------------------------------------------------------------------------
+operation +proj=pipeline
+ +step +proj=cart +ellps=GRS80
+ +step +proj=deformation
+ +xy_grids=tests/nkgrf03vel_realigned_xy_extract.ct2
+ +z_grids=tests/nkgrf03vel_realigned_z_extract.gtx +ellps=GRS80 +dt=1
+ +step +proj=cart +ellps=GRS80 +inv
+-------------------------------------------------------------------------------
+tolerance 0.05 mm
+accept 21.5 63 0
+expect 21.5000000049 62.9999999937 0.0083
+roundtrip 5
+
+-------------------------------------------------------------------------------
+Test with an extract of nkgrf03vel_realigned with GeoTIFF
+-------------------------------------------------------------------------------
+operation +proj=pipeline
+ +step +proj=cart +ellps=GRS80
+ +step +proj=deformation
+ +grids=tests/nkgrf03vel_realigned_extract.tif +ellps=GRS80 +dt=1
+ +step +proj=cart +ellps=GRS80 +inv
+-------------------------------------------------------------------------------
+tolerance 0.05 mm
+accept 21.5 63 0
+expect 21.5000000049 62.9999999937 0.0083
+roundtrip 5
+
+-------------------------------------------------------------------------------
Test the +dt parameter
-------------------------------------------------------------------------------
operation +proj=deformation +xy_grids=alaska +z_grids=egm96_15.gtx
diff --git a/test/gie/geotiff_grids.gie b/test/gie/geotiff_grids.gie
new file mode 100644
index 00000000..62a5b16d
--- /dev/null
+++ b/test/gie/geotiff_grids.gie
@@ -0,0 +1,324 @@
+
+-------------------------------------------------------------------------------
+===============================================================================
+Test GeoTIFF grids
+===============================================================================
+
+<gie>
+
+# Those first tests using +proj=vgridshift only test the capability of reading
+# correctly a value from various formulations of GeoTIFF file, hence only the
+# forward path is tested (reverse path is tested in other files)
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_pixelispoint.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_pixelisarea.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_deflate.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_deflate_floatingpointpredictor.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_uint16.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_uint16_with_scale_offset.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_int16.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_int32.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_uint32.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_float64.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+# The overview should be ignored
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_with_overview.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_in_second_channel.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_bigtiff.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_bigendian.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_bigendian_bigtiff.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_bottomup_with_scale.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_bottomup_with_matrix.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_with_subgrid.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.5 52.5 0
+expect 4.5 52.5 11.5
+
+# In subgrid
+accept 5.5 53.5 0
+expect 5.5 53.5 110.0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_nodata.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept 4.05 52.1 0
+expect 4.05 52.1 10
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_invalid_channel_type.tif +multiplier=1
+-------------------------------------------------------------------------------
+expect failure errno failed_to_load_grid
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_vgrid_unsupported_byte.tif +multiplier=1
+-------------------------------------------------------------------------------
+expect failure errno failed_to_load_grid
+-------------------------------------------------------------------------------
+
+
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_separate.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_strip.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_tiled.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_tiled_separate.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_positive_west.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_lon_shift_first.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_radian.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_degree.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+# The overview should be ignored
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_with_overview.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_extra_ifd_with_other_info.tif
+-------------------------------------------------------------------------------
+tolerance 2 mm
+accept 4.5 52.5 0
+expect 5.875 55.375 0
+-------------------------------------------------------------------------------
+
+# Subset of NTv2_0.gsb
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_with_subgrid.tif
+-------------------------------------------------------------------------------
+# In subgrid ALbanff, of parent CAwest
+accept -115.5416667 51.1666667 0
+expect -115.5427092888 51.1666899972 0
+
+# In subgrid ONtronto, of parent CAeast
+accept -80.5041667 44.5458333 0
+expect -80.50401615833 44.5458827236 0
+-------------------------------------------------------------------------------
+
+# Subset of NTv2_0.gsb
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_with_subgrid_no_grid_name.tif
+-------------------------------------------------------------------------------
+# In subgrid ALbanff, of parent CAwest
+accept -115.5416667 51.1666667 0
+expect -115.5427092888 51.1666899972 0
+
+# In subgrid ONtronto, of parent CAeast
+accept -80.5041667 44.5458333 0
+expect -80.50401615833 44.5458827236 0
+-------------------------------------------------------------------------------
+
+# Check a nested grid of a nested grid only based on spatial extent analysis
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif
+-------------------------------------------------------------------------------
+accept -45.0 22.5
+accept -44.9983333334 22.5013888889
+
+# Check a nested grid of a nested grid only based on spatial extent analysis
+-------------------------------------------------------------------------------
+operation +proj=vgridshift +grids=tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif +multiplier=1
+-------------------------------------------------------------------------------
+accept -45.0 22.5 0
+accept -45.0 22.5 5
+
+-------------------------------------------------------------------------------
+operation +proj=hgridshift +grids=tests/test_vgrid.tif
+-------------------------------------------------------------------------------
+expect failure errno failed_to_load_grid
+-------------------------------------------------------------------------------
+
+
+# IGNF:LAMBE to IGNF:LAMB93 using xyzgridshift operation
+-------------------------------------------------------------------------------
+operation +proj=pipeline
+ +step +inv +proj=lcc +lat_1=46.8 +lat_0=46.8 +lon_0=0
+ +k_0=0.99987742 +x_0=600000 +y_0=2200000 +ellps=clrk80ign +pm=paris
+ +step +proj=push +v_3
+ +step +proj=cart +ellps=clrk80ign
+ +step +proj=xyzgridshift +grids=tests/subset_of_gr3df97a.tif +grid_ref=output_crs +ellps=GRS80
+ +step +proj=cart +ellps=GRS80 +inv
+ +step +proj=pop +v_3
+ +step +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44
+ +x_0=700000 +y_0=6600000 +ellps=GRS80
+-------------------------------------------------------------------------------
+tolerance 1 mm
+
+accept 814149.529 1887019.768 0
+expect 860690.804 6319036.849 0
+# If using ntf_r93.gsb, one gets: 860690.805 6319036.850
+
+roundtrip 1
+-------------------------------------------------------------------------------
+
+
+</gie>
diff --git a/test/gigs/Makefile.am b/test/gigs/Makefile.am
index a66052db..436a6e89 100644
--- a/test/gigs/Makefile.am
+++ b/test/gigs/Makefile.am
@@ -23,54 +23,54 @@ EXTRA_DIST = \
PROJ_LIB ?= ../../data/for_tests
5101.1: 5101.1-jhs.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5101.2: 5101.2-jhs.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5101.3: 5101.3-jhs.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5101.4: 5101.4-jhs-etmerc.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5102.1: 5102.1.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5103.1: 5103.1.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5103.2: 5103.2.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5103.3: 5103.3.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5105.2: 5105.2.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5106: 5106.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5107: 5107.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5109: 5109.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5111.1: 5111.1.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5112: 5112.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5113: 5113.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5201: 5201.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
5208: 5208.gie
- PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) $(GIEEXE) $<
check-local: 5101.1 5101.2 5101.3 5101.4 5102.1 5103.1 5103.2 5103.3 5105.2 5106 5107 5109 5111.1 5112 5113 5201 5208
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 841d72b3..845d07e5 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -71,7 +71,7 @@ target_link_libraries(proj_pj_transform_test
${PROJ_LIBRARIES})
add_test(NAME proj_pj_transform_test COMMAND proj_pj_transform_test)
set_property(TEST proj_pj_transform_test
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
add_executable(proj_errno_string_test
@@ -82,7 +82,7 @@ target_link_libraries(proj_errno_string_test
${PROJ_LIBRARIES})
add_test(NAME proj_errno_string_test COMMAND proj_errno_string_test)
set_property(TEST proj_errno_string_test
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
add_executable(proj_angular_io_test
main.cpp
@@ -92,7 +92,7 @@ target_link_libraries(proj_angular_io_test
${PROJ_LIBRARIES})
add_test(NAME proj_angular_io_test COMMAND proj_angular_io_test)
set_property(TEST proj_angular_io_test
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
add_executable(proj_context_test
main.cpp
@@ -102,7 +102,7 @@ target_link_libraries(proj_context_test
${PROJ_LIBRARIES})
add_test(NAME proj_context_test COMMAND proj_context_test)
set_property(TEST proj_context_test
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
if(MSVC AND BUILD_LIBPROJ_SHARED)
# ph_phi2_test not compatible of a .dll build
@@ -115,7 +115,7 @@ else()
${PROJ_LIBRARIES})
add_test(NAME pj_phi2_test COMMAND pj_phi2_test)
set_property(TEST pj_phi2_test
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
endif()
add_executable(proj_test_cpp_api
@@ -128,14 +128,15 @@ add_executable(proj_test_cpp_api
test_operation.cpp
test_datum.cpp
test_factory.cpp
- test_c_api.cpp)
+ test_c_api.cpp
+ test_grids.cpp)
target_link_libraries(proj_test_cpp_api
GTest::gtest
${PROJ_LIBRARIES}
${SQLITE3_LIBRARY})
add_test(NAME proj_test_cpp_api COMMAND proj_test_cpp_api)
set_property(TEST proj_test_cpp_api
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
add_executable(gie_self_tests
@@ -146,4 +147,20 @@ target_link_libraries(gie_self_tests
${PROJ_LIBRARIES})
add_test(NAME gie_self_tests COMMAND gie_self_tests)
set_property(TEST gie_self_tests
- PROPERTY ENVIRONMENT "PROJ_LIB=${PROJECT_BINARY_DIR}/data")
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests")
+
+
+add_executable(test_network
+ main.cpp
+ test_network.cpp)
+if(CURL_FOUND)
+ include_directories(${CURL_INCLUDE_DIR})
+ target_link_libraries(test_network ${CURL_LIBRARY})
+endif()
+target_link_libraries(test_network
+ GTest::gtest
+ ${PROJ_LIBRARIES}
+ ${SQLITE3_LIBRARY})
+add_test(NAME test_network COMMAND test_network)
+set_property(TEST test_network
+ PROPERTY ENVIRONMENT "PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES;PROJ_LIB=${PROJECT_BINARY_DIR}/data/for_tests;PROJ_SOURCE_DATA=${PROJECT_SOURCE_DIR}/data")
diff --git a/test/unit/Makefile.am b/test/unit/Makefile.am
index b7196828..11a6473c 100644
--- a/test/unit/Makefile.am
+++ b/test/unit/Makefile.am
@@ -17,12 +17,13 @@ noinst_PROGRAMS += proj_context_test
noinst_PROGRAMS += test_cpp_api
noinst_PROGRAMS += gie_self_tests
noinst_PROGRAMS += include_proj_h_from_c
+noinst_PROGRAMS += test_network
pj_transform_test_SOURCES = pj_transform_test.cpp main.cpp
pj_transform_test_LDADD = ../../src/libproj.la @GTEST_LIBS@
pj_transform_test-check: pj_transform_test
- PROJ_LIB=$(PROJ_LIB) ./pj_transform_test
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) ./pj_transform_test
pj_phi2_test_SOURCES = pj_phi2_test.cpp main.cpp
pj_phi2_test_LDADD = ../../src/libproj.la @GTEST_LIBS@
@@ -46,20 +47,27 @@ proj_context_test_SOURCES = proj_context_test.cpp main.cpp
proj_context_test_LDADD = ../../src/libproj.la @GTEST_LIBS@
proj_context_test-check: proj_context_test
- ./proj_context_test
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES ./proj_context_test
-test_cpp_api_SOURCES = test_util.cpp test_common.cpp test_crs.cpp test_metadata.cpp test_io.cpp test_operation.cpp test_datum.cpp test_factory.cpp test_c_api.cpp main.cpp
+test_cpp_api_SOURCES = test_util.cpp test_common.cpp test_crs.cpp test_metadata.cpp test_io.cpp test_operation.cpp test_datum.cpp test_factory.cpp test_c_api.cpp test_grids.cpp main.cpp
test_cpp_api_LDADD = ../../src/libproj.la @GTEST_LIBS@ @SQLITE3_LIBS@
test_cpp_api-check: test_cpp_api
- PROJ_LIB=$(PROJ_LIB) ./test_cpp_api
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) ./test_cpp_api
gie_self_tests_SOURCES = gie_self_tests.cpp main.cpp
gie_self_tests_LDADD = ../../src/libproj.la @GTEST_LIBS@ @SQLITE3_LIBS@
gie_self_tests-check: gie_self_tests
- PROJ_LIB=$(PROJ_LIB) ./gie_self_tests
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) ./gie_self_tests
include_proj_h_from_c_SOURCES = include_proj_h_from_c.c
-check-local: pj_transform_test-check pj_phi2_test-check proj_errno_string_test-check proj_angular_io_test-check proj_context_test-check test_cpp_api-check gie_self_tests-check
+test_network_SOURCES = test_network.cpp main.cpp
+test_network_CXXFLAGS = @CURL_CFLAGS@ @CURL_ENABLED_FLAGS@
+test_network_LDADD = ../../src/libproj.la @GTEST_LIBS@ @SQLITE3_LIBS@ @CURL_LIBS@
+
+test_network-check: test_network
+ PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES PROJ_LIB=$(PROJ_LIB) PROJ_SOURCE_DATA=$(PROJ_LIB) ./test_network
+
+check-local: pj_transform_test-check pj_phi2_test-check proj_errno_string_test-check proj_angular_io_test-check proj_context_test-check test_cpp_api-check gie_self_tests-check test_network-check
diff --git a/test/unit/gie_self_tests.cpp b/test/unit/gie_self_tests.cpp
index a738db75..bcf31139 100644
--- a/test/unit/gie_self_tests.cpp
+++ b/test/unit/gie_self_tests.cpp
@@ -348,7 +348,9 @@ TEST(gie, info_functions) {
/* proj_info() */
/* this one is difficult to test, since the output changes with the setup */
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY="));
info = proj_info();
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES"));
if (info.version[0] != '\0') {
char tmpstr[64];
@@ -360,6 +362,9 @@ TEST(gie, info_functions) {
ASSERT_NE(std::string(info.searchpath), std::string());
}
+ ASSERT_TRUE(std::string(info.searchpath).find("/proj") !=
+ std::string::npos);
+
/* proj_pj_info() */
{
P = proj_create(PJ_DEFAULT_CTX,
diff --git a/test/unit/pj_transform_test.cpp b/test/unit/pj_transform_test.cpp
index 1f4473c1..ddb054f0 100644
--- a/test/unit/pj_transform_test.cpp
+++ b/test/unit/pj_transform_test.cpp
@@ -616,6 +616,22 @@ TEST(proj_api_h, pj_set_finder) {
// ---------------------------------------------------------------------------
+TEST(proj_api_h, default_fileapi) {
+ auto ctx = pj_ctx_alloc();
+ auto fid = pj_open_lib(ctx, "proj.db", "rb");
+ ASSERT_NE(fid, nullptr);
+ char header[6];
+ ASSERT_EQ(pj_ctx_fread(ctx, header, 1, 6, fid), 6U);
+ ASSERT_TRUE(memcmp(header, "SQLite", 6) == 0);
+ ASSERT_EQ(pj_ctx_ftell(ctx, fid), 6);
+ ASSERT_EQ(pj_ctx_fseek(ctx, fid, 0, SEEK_SET), 0);
+ ASSERT_EQ(pj_ctx_ftell(ctx, fid), 0);
+ pj_ctx_fclose(ctx, fid);
+ pj_ctx_free(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
TEST(pj_transform_test, ob_tran_to_meter_as_dest) {
auto src = pj_init_plus(
"+ellps=WGS84 +a=57.29577951308232 +proj=eqc +lon_0=0.0 +no_defs");
@@ -633,6 +649,79 @@ TEST(pj_transform_test, ob_tran_to_meter_as_dest) {
// ---------------------------------------------------------------------------
+struct Spy {
+ bool gotInMyFOpen = false;
+ bool gotInMyFRead = false;
+ bool gotInMyFSeek = false;
+ bool gotInMyFTell = false;
+ bool gotInMyFClose = false;
+};
+
+struct MyFile {
+ FILE *fp;
+ Spy *spy;
+};
+
+static PAFile myFOpen(projCtx ctx, const char *filename, const char *access) {
+ FILE *fp = fopen(filename, access);
+ if (!fp)
+ return nullptr;
+ MyFile *myF = new MyFile;
+ myF->spy = (Spy *)pj_ctx_get_app_data(ctx);
+ myF->spy->gotInMyFOpen = true;
+ myF->fp = fp;
+ return reinterpret_cast<PAFile>(myF);
+}
+
+static size_t myFRead(void *buffer, size_t size, size_t nmemb, PAFile file) {
+ MyFile *myF = reinterpret_cast<MyFile *>(file);
+ myF->spy->gotInMyFRead = true;
+ return fread(buffer, size, nmemb, myF->fp);
+}
+
+static int myFSeek(PAFile file, long offset, int whence) {
+ MyFile *myF = reinterpret_cast<MyFile *>(file);
+ myF->spy->gotInMyFSeek = true;
+ return fseek(myF->fp, offset, whence);
+}
+
+static long myFTell(PAFile file) {
+ MyFile *myF = reinterpret_cast<MyFile *>(file);
+ myF->spy->gotInMyFTell = true;
+ return ftell(myF->fp);
+}
+
+static void myFClose(PAFile file) {
+ MyFile *myF = reinterpret_cast<MyFile *>(file);
+ myF->spy->gotInMyFClose = true;
+ fclose(myF->fp);
+ delete myF;
+}
+
+TEST(proj_api_h, custom_fileapi) {
+ auto ctx = pj_ctx_alloc();
+ Spy spy;
+ pj_ctx_set_app_data(ctx, &spy);
+ projFileAPI myAPI = {myFOpen, myFRead, myFSeek, myFTell, myFClose};
+ pj_ctx_set_fileapi(ctx, &myAPI);
+ EXPECT_EQ(pj_ctx_get_fileapi(ctx), &myAPI);
+ auto fid = pj_open_lib(ctx, "proj.db", "rb");
+ ASSERT_NE(fid, nullptr);
+ char header[6];
+ ASSERT_EQ(pj_ctx_fread(ctx, header, 1, 6, fid), 6U);
+ ASSERT_TRUE(memcmp(header, "SQLite", 6) == 0);
+ ASSERT_EQ(pj_ctx_ftell(ctx, fid), 6);
+ ASSERT_EQ(pj_ctx_fseek(ctx, fid, 0, SEEK_SET), 0);
+ ASSERT_EQ(pj_ctx_ftell(ctx, fid), 0);
+ pj_ctx_fclose(ctx, fid);
+ pj_ctx_free(ctx);
+ EXPECT_TRUE(spy.gotInMyFOpen);
+ EXPECT_TRUE(spy.gotInMyFRead);
+ EXPECT_TRUE(spy.gotInMyFSeek);
+ EXPECT_TRUE(spy.gotInMyFTell);
+ EXPECT_TRUE(spy.gotInMyFClose);
+}
+
TEST(pj_transform_test, ob_tran_to_meter_as_srouce) {
auto src = pj_init_plus("+ellps=WGS84 +proj=ob_tran +o_proj=latlon "
"+o_lon_p=0.0 +o_lat_p=90.0 +lon_0=360.0 "
diff --git a/test/unit/proj_context_test.cpp b/test/unit/proj_context_test.cpp
index 23c46f29..92f251cd 100644
--- a/test/unit/proj_context_test.cpp
+++ b/test/unit/proj_context_test.cpp
@@ -40,7 +40,20 @@
namespace {
-static std::string createTempDict(std::string &dirname) {
+static bool createTmpFile(const std::string &filename) {
+ FILE *f = fopen(filename.c_str(), "wt");
+ if (!f)
+ return false;
+ fprintf(
+ f,
+ "<MY_PIPELINE> +proj=pipeline +step +proj=utm +zone=31 +ellps=GRS80\n");
+ fclose(f);
+ return true;
+}
+
+// ---------------------------------------------------------------------------
+
+static std::string createTempDict(std::string &dirname, const char *filename) {
const char *temp_dir = getenv("TEMP");
if (!temp_dir) {
temp_dir = getenv("TMP");
@@ -58,16 +71,9 @@ static std::string createTempDict(std::string &dirname) {
std::string tmpFilename;
tmpFilename = temp_dir;
tmpFilename += DIR_CHAR;
- tmpFilename += "temp_proj_dic";
+ tmpFilename += filename;
- FILE *f = fopen(tmpFilename.c_str(), "wt");
- if (!f)
- return std::string();
- fprintf(
- f,
- "<MY_PIPELINE> +proj=pipeline +step +proj=utm +zone=31 +ellps=GRS80\n");
- fclose(f);
- return tmpFilename;
+ return createTmpFile(tmpFilename) ? tmpFilename : std::string();
}
// ---------------------------------------------------------------------------
@@ -85,7 +91,7 @@ static int MyUnlink(const std::string &filename) {
TEST(proj_context, proj_context_set_file_finder) {
std::string dirname;
- auto filename = createTempDict(dirname);
+ auto filename = createTempDict(dirname, "temp_proj_dic1");
if (filename.empty())
return;
@@ -111,7 +117,7 @@ TEST(proj_context, proj_context_set_file_finder) {
finderData.dirname = dirname;
proj_context_set_file_finder(ctx, finder, &finderData);
- auto P = proj_create(ctx, "+init=temp_proj_dic:MY_PIPELINE");
+ auto P = proj_create(ctx, "+init=temp_proj_dic1:MY_PIPELINE");
EXPECT_NE(P, nullptr);
proj_destroy(P);
@@ -125,7 +131,7 @@ TEST(proj_context, proj_context_set_file_finder) {
TEST(proj_context, proj_context_set_search_paths) {
std::string dirname;
- auto filename = createTempDict(dirname);
+ auto filename = createTempDict(dirname, "temp_proj_dic2");
if (filename.empty())
return;
@@ -134,7 +140,7 @@ TEST(proj_context, proj_context_set_search_paths) {
const char *path = dirname.c_str();
proj_context_set_search_paths(ctx, 1, &path);
- auto P = proj_create(ctx, "+init=temp_proj_dic:MY_PIPELINE");
+ auto P = proj_create(ctx, "+init=temp_proj_dic2:MY_PIPELINE");
EXPECT_NE(P, nullptr);
proj_destroy(P);
@@ -143,4 +149,33 @@ TEST(proj_context, proj_context_set_search_paths) {
MyUnlink(filename);
}
+// ---------------------------------------------------------------------------
+
+TEST(proj_context, read_grid_from_user_writable_directory) {
+
+ auto ctx = proj_context_create();
+ auto path = pj_context_get_user_writable_directory(ctx, true);
+ EXPECT_TRUE(!path.empty());
+ auto filename = path + DIR_CHAR + "temp_proj_dic3";
+ EXPECT_TRUE(createTmpFile(filename));
+ {
+ // Check that with PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES (set by
+ // calling script), we cannot find the file
+ auto P = proj_create(ctx, "+init=temp_proj_dic3:MY_PIPELINE");
+ EXPECT_EQ(P, nullptr);
+ proj_destroy(P);
+ }
+ {
+ // Cancel the effect of PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY="));
+ auto P = proj_create(ctx, "+init=temp_proj_dic3:MY_PIPELINE");
+ EXPECT_NE(P, nullptr);
+ putenv(
+ const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES"));
+ proj_destroy(P);
+ }
+ proj_context_destroy(ctx);
+ MyUnlink(filename);
+}
+
} // namespace
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index a657c61e..5c6dabba 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -4467,6 +4467,27 @@ TEST_F(CApi, proj_create_derived_geographic_crs) {
// ---------------------------------------------------------------------------
+TEST_F(CApi, proj_context_set_sqlite3_vfs_name) {
+
+ PJ_CONTEXT *ctx = proj_context_create();
+ proj_log_func(ctx, nullptr, [](void *, int, const char *) -> void {});
+
+ // Set a dummy VFS and check it is taken into account
+ // (failure to open proj.db)
+ proj_context_set_sqlite3_vfs_name(ctx, "dummy_vfs_name");
+ ASSERT_EQ(proj_create(ctx, "EPSG:4326"), nullptr);
+
+ // Restore default VFS
+ proj_context_set_sqlite3_vfs_name(ctx, nullptr);
+ PJ *crs_4326 = proj_create(ctx, "EPSG:4326");
+ ASSERT_NE(crs_4326, nullptr);
+ proj_destroy(crs_4326);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
TEST_F(CApi, proj_is_equivalent_to_with_ctx) {
auto from_epsg = proj_create_from_database(m_ctxt, "EPSG", "7844",
PJ_CATEGORY_CRS, false, nullptr);
diff --git a/test/unit/test_factory.cpp b/test/unit/test_factory.cpp
index 8ae58c96..6a88e2b6 100644
--- a/test/unit/test_factory.cpp
+++ b/test/unit/test_factory.cpp
@@ -1617,34 +1617,34 @@ class FactoryWithTmpDatabase : public ::testing::Test {
DatabaseContext::create(m_ctxt), "OTHER");
auto res = factoryOTHER->createFromCRSCodesWithIntermediates(
"NS_SOURCE", "SOURCE", "NS_TARGET", "TARGET", false, false,
- false, {});
+ false, false, {});
EXPECT_EQ(res.size(), 1U);
EXPECT_TRUE(res.empty() ||
nn_dynamic_pointer_cast<ConcatenatedOperation>(res[0]));
res = factoryOTHER->createFromCRSCodesWithIntermediates(
"NS_SOURCE", "SOURCE", "NS_TARGET", "TARGET", false, false,
- false, {std::make_pair(std::string("NS_PIVOT"),
- std::string("PIVOT"))});
+ false, false, {std::make_pair(std::string("NS_PIVOT"),
+ std::string("PIVOT"))});
EXPECT_EQ(res.size(), 1U);
EXPECT_TRUE(res.empty() ||
nn_dynamic_pointer_cast<ConcatenatedOperation>(res[0]));
res = factoryOTHER->createFromCRSCodesWithIntermediates(
"NS_SOURCE", "SOURCE", "NS_TARGET", "TARGET", false, false,
- false, {std::make_pair(std::string("NS_PIVOT"),
- std::string("NOT_EXISTING"))});
+ false, false, {std::make_pair(std::string("NS_PIVOT"),
+ std::string("NOT_EXISTING"))});
EXPECT_EQ(res.size(), 0U);
res = factoryOTHER->createFromCRSCodesWithIntermediates(
"NS_SOURCE", "SOURCE", "NS_TARGET", "TARGET", false, false,
- false,
+ false, false,
{std::make_pair(std::string("BAD_NS"), std::string("PIVOT"))});
EXPECT_EQ(res.size(), 0U);
res = factoryOTHER->createFromCRSCodesWithIntermediates(
"NS_TARGET", "TARGET", "NS_SOURCE", "SOURCE", false, false,
- false, {});
+ false, false, {});
EXPECT_EQ(res.size(), 1U);
EXPECT_TRUE(res.empty() ||
nn_dynamic_pointer_cast<ConcatenatedOperation>(res[0]));
@@ -1654,7 +1654,7 @@ class FactoryWithTmpDatabase : public ::testing::Test {
DatabaseContext::create(m_ctxt), std::string());
auto res = factory->createFromCRSCodesWithIntermediates(
"NS_SOURCE", "SOURCE", "NS_TARGET", "TARGET", false, false,
- false, {});
+ false, false, {});
EXPECT_EQ(res.size(), 1U);
EXPECT_TRUE(res.empty() ||
nn_dynamic_pointer_cast<ConcatenatedOperation>(res[0]));
@@ -1833,7 +1833,7 @@ TEST(factory, AuthorityFactory_createFromCoordinateReferenceSystemCodes) {
{
// Test removal of superseded transform
auto list = factory->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4179", "EPSG", "4258", false, false, true);
+ "EPSG", "4179", "EPSG", "4258", false, false, false, true);
ASSERT_EQ(list.size(), 2U);
// Romania has a larger area than Poland (given our approx formula)
EXPECT_EQ(list[0]->getEPSGCode(), 15994); // Romania - 3m
@@ -1851,12 +1851,12 @@ TEST(
{
auto res = factory->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "32631", false, false, false);
+ "EPSG", "4326", "EPSG", "32631", false, false, false, false);
ASSERT_EQ(res.size(), 1U);
}
{
auto res = factory->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4209", "EPSG", "4326", false, false, false);
+ "EPSG", "4209", "EPSG", "4326", false, false, false, false);
EXPECT_TRUE(!res.empty());
for (const auto &conv : res) {
EXPECT_TRUE(conv->sourceCRS()->getEPSGCode() == 4209);
@@ -1889,7 +1889,8 @@ TEST_F(FactoryWithTmpDatabase,
DatabaseContext::create(m_ctxt), std::string());
{
auto res = factoryGeneral->createFromCoordinateReferenceSystemCodes(
- "OTHER", "OTHER_4326", "OTHER", "OTHER_32631", false, false, false);
+ "OTHER", "OTHER_4326", "OTHER", "OTHER_32631", false, false, false,
+ false);
ASSERT_EQ(res.size(), 1U);
}
@@ -1897,7 +1898,8 @@ TEST_F(FactoryWithTmpDatabase,
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "EPSG");
{
auto res = factoryEPSG->createFromCoordinateReferenceSystemCodes(
- "OTHER", "OTHER_4326", "OTHER", "OTHER_32631", false, false, false);
+ "OTHER", "OTHER_4326", "OTHER", "OTHER_32631", false, false, false,
+ false);
ASSERT_EQ(res.size(), 1U);
}
@@ -1919,17 +1921,17 @@ TEST_F(FactoryWithTmpDatabase,
<< last_error();
{
auto res = factoryGeneral->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false);
+ "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false, false);
ASSERT_EQ(res.size(), 1U);
}
{
auto res = factoryEPSG->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false);
+ "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false, false);
ASSERT_EQ(res.size(), 0U);
}
{
auto res = factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false);
+ "EPSG", "4326", "OTHER", "OTHER_4326", false, false, false, false);
ASSERT_EQ(res.size(), 1U);
}
}
@@ -1982,7 +1984,7 @@ TEST_F(FactoryWithTmpDatabase,
auto factoryOTHER =
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "OTHER");
auto res = factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "4326", false, false, false);
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false);
ASSERT_EQ(res.size(), 3U);
EXPECT_EQ(*(res[0]->name()->description()), "TRANSFORMATION_1M");
EXPECT_EQ(*(res[1]->name()->description()), "TRANSFORMATION_10M");
@@ -2001,7 +2003,7 @@ TEST_F(
auto factory = AuthorityFactory::create(DatabaseContext::create(m_ctxt),
std::string());
auto res = factory->createFromCRSCodesWithIntermediates(
- "EPSG", "4326", "EPSG", "4326", false, false, false, {});
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false, {});
EXPECT_EQ(res.size(), 0U);
}
@@ -2085,7 +2087,7 @@ TEST_F(FactoryWithTmpDatabase, AuthorityFactory_proj_based_transformation) {
auto factoryOTHER =
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "OTHER");
auto res = factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "4326", false, false, false);
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false);
ASSERT_EQ(res.size(), 1U);
EXPECT_EQ(res[0]->nameStr(), "My PROJ string based op");
EXPECT_EQ(res[0]->exportToPROJString(PROJStringFormatter::create().get()),
@@ -2146,7 +2148,7 @@ TEST_F(FactoryWithTmpDatabase, AuthorityFactory_wkt_based_transformation) {
auto factoryOTHER =
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "OTHER");
auto res = factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "4326", false, false, false);
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false);
ASSERT_EQ(res.size(), 1U);
EXPECT_EQ(res[0]->nameStr(), "My WKT string based op");
EXPECT_EQ(res[0]->exportToPROJString(PROJStringFormatter::create().get()),
@@ -2180,9 +2182,10 @@ TEST_F(FactoryWithTmpDatabase,
auto factoryOTHER =
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "OTHER");
- EXPECT_THROW(factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "4326", false, false, false),
- FactoryException);
+ EXPECT_THROW(
+ factoryOTHER->createFromCoordinateReferenceSystemCodes(
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false),
+ FactoryException);
}
// ---------------------------------------------------------------------------
@@ -2207,9 +2210,10 @@ TEST_F(FactoryWithTmpDatabase,
auto factoryOTHER =
AuthorityFactory::create(DatabaseContext::create(m_ctxt), "OTHER");
- EXPECT_THROW(factoryOTHER->createFromCoordinateReferenceSystemCodes(
- "EPSG", "4326", "EPSG", "4326", false, false, false),
- FactoryException);
+ EXPECT_THROW(
+ factoryOTHER->createFromCoordinateReferenceSystemCodes(
+ "EPSG", "4326", "EPSG", "4326", false, false, false, false),
+ FactoryException);
}
// ---------------------------------------------------------------------------
@@ -2262,7 +2266,7 @@ TEST_F(FactoryWithTmpDatabase, lookForGridInfo) {
bool openLicense = false;
bool gridAvailable = false;
EXPECT_TRUE(DatabaseContext::create(m_ctxt)->lookForGridInfo(
- "PROJ_fake_grid", fullFilename, packageName, url, directDownload,
+ "PROJ_fake_grid", false, fullFilename, packageName, url, directDownload,
openLicense, gridAvailable));
EXPECT_TRUE(fullFilename.empty());
EXPECT_TRUE(packageName.empty());
diff --git a/test/unit/test_grids.cpp b/test/unit/test_grids.cpp
new file mode 100644
index 00000000..5240949e
--- /dev/null
+++ b/test/unit/test_grids.cpp
@@ -0,0 +1,227 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: Test grids.hpp
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2020, Even Rouault <even dot rouault at spatialys dot 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.
+ ****************************************************************************/
+
+#include "gtest_include.h"
+
+#include "grids.hpp"
+
+#include "proj_internal.h" // M_PI
+
+namespace {
+
+// ---------------------------------------------------------------------------
+
+class GridTest : public ::testing::Test {
+
+ static void DummyLogFunction(void *, int, const char *) {}
+
+ protected:
+ void SetUp() override {
+ m_ctxt = proj_context_create();
+ proj_log_func(m_ctxt, nullptr, DummyLogFunction);
+ m_ctxt2 = proj_context_create();
+ proj_log_func(m_ctxt2, nullptr, DummyLogFunction);
+ }
+
+ void TearDown() override {
+ proj_context_destroy(m_ctxt);
+ proj_context_destroy(m_ctxt2);
+ }
+
+ PJ_CONTEXT *m_ctxt = nullptr;
+ PJ_CONTEXT *m_ctxt2 = nullptr;
+};
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, VerticalShiftGridSet_null) {
+ auto gridSet = NS_PROJ::VerticalShiftGridSet::open(m_ctxt, "null");
+ ASSERT_NE(gridSet, nullptr);
+ auto grid = gridSet->gridAt(0.0, 0.0);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 3);
+ EXPECT_EQ(grid->height(), 3);
+ EXPECT_EQ(grid->extentAndRes().westLon, -M_PI);
+ EXPECT_TRUE(grid->isNullGrid());
+ EXPECT_FALSE(grid->hasChanged());
+ float out = -1.0f;
+ EXPECT_TRUE(grid->valueAt(0, 0, out));
+ EXPECT_EQ(out, 0.0f);
+ EXPECT_FALSE(grid->isNodata(0.0f, 0.0));
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, VerticalShiftGridSet_gtx) {
+ ASSERT_EQ(NS_PROJ::VerticalShiftGridSet::open(m_ctxt, "foobar"), nullptr);
+ auto gridSet =
+ NS_PROJ::VerticalShiftGridSet::open(m_ctxt, "tests/test_nodata.gtx");
+ ASSERT_NE(gridSet, nullptr);
+ ASSERT_EQ(gridSet->gridAt(-100, -100), nullptr);
+ auto grid = gridSet->gridAt(4.15 / 180 * M_PI, 52.15 / 180 * M_PI);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_TRUE(grid->isNodata(-88.8888f, 1.0));
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+ grid = gridSet->gridAt(4.15 / 180 * M_PI, 52.15 / 180 * M_PI);
+ EXPECT_NE(grid, nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, HorizontalShiftGridSet_null) {
+ auto gridSet = NS_PROJ::HorizontalShiftGridSet::open(m_ctxt, "null");
+ ASSERT_NE(gridSet, nullptr);
+ auto grid = gridSet->gridAt(0.0, 0.0);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 3);
+ EXPECT_EQ(grid->height(), 3);
+ EXPECT_EQ(grid->extentAndRes().westLon, -M_PI);
+ EXPECT_TRUE(grid->isNullGrid());
+ EXPECT_FALSE(grid->hasChanged());
+ float out1 = -1.0f;
+ float out2 = -1.0f;
+ EXPECT_TRUE(grid->valueAt(0, 0, false, out1, out2));
+ EXPECT_EQ(out1, 0.0f);
+ EXPECT_EQ(out2, 0.0f);
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, HorizontalShiftGridSet_gtiff) {
+ auto gridSet =
+ NS_PROJ::HorizontalShiftGridSet::open(m_ctxt, "tests/test_hgrid.tif");
+ ASSERT_NE(gridSet, nullptr);
+ EXPECT_EQ(gridSet->format(), "gtiff");
+ EXPECT_TRUE(gridSet->name().find("tests/test_hgrid.tif") !=
+ std::string::npos)
+ << gridSet->name();
+ EXPECT_EQ(gridSet->grids().size(), 1U);
+ ASSERT_EQ(gridSet->gridAt(-100, -100), nullptr);
+ auto grid = gridSet->gridAt(5.5 / 180 * M_PI, 53.5 / 180 * M_PI);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 4);
+ EXPECT_EQ(grid->height(), 4);
+ EXPECT_EQ(grid->extentAndRes().westLon, 4.0 / 180 * M_PI);
+ EXPECT_FALSE(grid->isNullGrid());
+ EXPECT_FALSE(grid->hasChanged());
+ float out1 = -1.0f;
+ float out2 = -1.0f;
+ EXPECT_TRUE(grid->valueAt(0, 3, false, out1, out2));
+ EXPECT_EQ(out1, static_cast<float>(14400.0 / 3600. / 180 * M_PI));
+ EXPECT_EQ(out2, static_cast<float>(900.0 / 3600. / 180 * M_PI));
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+ grid = gridSet->gridAt(5.5 / 180 * M_PI, 53.5 / 180 * M_PI);
+ EXPECT_NE(grid, nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, GenericShiftGridSet_null) {
+ auto gridSet = NS_PROJ::GenericShiftGridSet::open(m_ctxt, "null");
+ ASSERT_NE(gridSet, nullptr);
+ auto grid = gridSet->gridAt(0.0, 0.0);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 3);
+ EXPECT_EQ(grid->height(), 3);
+ EXPECT_EQ(grid->extentAndRes().westLon, -M_PI);
+ EXPECT_TRUE(grid->isNullGrid());
+ EXPECT_FALSE(grid->hasChanged());
+ float out = -1.0f;
+ EXPECT_TRUE(grid->valueAt(0, 0, 0, out));
+ EXPECT_EQ(out, 0.0f);
+ EXPECT_EQ(grid->unit(0), "");
+ EXPECT_EQ(grid->description(0), "");
+ EXPECT_EQ(grid->metadataItem("foo"), "");
+ EXPECT_EQ(grid->samplesPerPixel(), 0);
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, GenericShiftGridSet_gtiff) {
+ ASSERT_EQ(NS_PROJ::GenericShiftGridSet::open(m_ctxt, "foobar"), nullptr);
+ auto gridSet = NS_PROJ::GenericShiftGridSet::open(
+ m_ctxt, "tests/nkgrf03vel_realigned_extract.tif");
+ ASSERT_NE(gridSet, nullptr);
+ ASSERT_EQ(gridSet->gridAt(-100, -100), nullptr);
+ auto grid = gridSet->gridAt(21.3333333 / 180 * M_PI, 63.0 / 180 * M_PI);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 5);
+ EXPECT_EQ(grid->height(), 5);
+ EXPECT_EQ(grid->extentAndRes().westLon, 21.0 / 180 * M_PI);
+ EXPECT_FALSE(grid->isNullGrid());
+ EXPECT_FALSE(grid->hasChanged());
+ float out = -1.0f;
+ EXPECT_FALSE(grid->valueAt(0, 0, grid->samplesPerPixel(), out));
+ EXPECT_EQ(grid->metadataItem("area_of_use"), "Nordic and Baltic countries");
+ EXPECT_EQ(grid->metadataItem("non_existing"), std::string());
+ EXPECT_EQ(grid->metadataItem("non_existing", 1), std::string());
+ EXPECT_EQ(grid->metadataItem("non_existing", 10), std::string());
+ gridSet->reassign_context(m_ctxt2);
+ gridSet->reopen(m_ctxt2);
+ grid = gridSet->gridAt(21.3333333 / 180 * M_PI, 63.0 / 180 * M_PI);
+ EXPECT_NE(grid, nullptr);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest, GenericShiftGridSet_gtiff_with_subgrid) {
+ auto gridSet = NS_PROJ::GenericShiftGridSet::open(
+ m_ctxt, "tests/test_hgrid_with_subgrid.tif");
+ ASSERT_NE(gridSet, nullptr);
+ ASSERT_EQ(gridSet->gridAt(-100, -100), nullptr);
+ auto grid =
+ gridSet->gridAt(-115.5416667 / 180 * M_PI, 51.1666667 / 180 * M_PI);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 11);
+ EXPECT_EQ(grid->height(), 21);
+ EXPECT_EQ(grid->metadataItem("grid_name"), "ALbanff");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(GridTest,
+ GenericShiftGridSet_gtiff_with_two_level_of_subgrids_no_grid_name) {
+ auto gridSet = NS_PROJ::GenericShiftGridSet::open(
+ m_ctxt, "tests/test_hgrid_with_two_level_of_subgrids_no_grid_name.tif");
+ ASSERT_NE(gridSet, nullptr);
+ ASSERT_EQ(gridSet->gridAt(-100, -100), nullptr);
+ auto grid = gridSet->gridAt(-45.5 / 180 * M_PI, 22.5 / 180 * M_PI);
+ ASSERT_NE(grid, nullptr);
+ EXPECT_EQ(grid->width(), 8);
+ EXPECT_EQ(grid->height(), 8);
+}
+
+} // namespace
diff --git a/test/unit/test_network.cpp b/test/unit/test_network.cpp
new file mode 100644
index 00000000..241e6bbc
--- /dev/null
+++ b/test/unit/test_network.cpp
@@ -0,0 +1,1856 @@
+/******************************************************************************
+ *
+ * Project: PROJ
+ * Purpose: Test networking
+ * Author: Even Rouault <even dot rouault at spatialys dot com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2019, Even Rouault <even dot rouault at spatialys dot 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.
+ ****************************************************************************/
+
+#include "gtest_include.h"
+
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "proj_internal.h"
+#include <proj.h>
+
+#include <sqlite3.h>
+#include <time.h>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifdef CURL_ENABLED
+#include <curl/curl.h>
+#endif
+
+namespace {
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+static bool networkAccessOK = false;
+
+static size_t noop_curl_write_func(void *, size_t, size_t nmemb, void *) {
+ return nmemb;
+}
+
+TEST(networking, initial_check) {
+ CURL *hCurlHandle = curl_easy_init();
+ if (!hCurlHandle)
+ return;
+ curl_easy_setopt(hCurlHandle, CURLOPT_URL,
+ "https://cdn.proj.org/ntf_r93.tif");
+
+ curl_easy_setopt(hCurlHandle, CURLOPT_RANGE, "0-1");
+ curl_easy_setopt(hCurlHandle, CURLOPT_WRITEFUNCTION, noop_curl_write_func);
+
+ curl_easy_perform(hCurlHandle);
+
+ long response_code = 0;
+ curl_easy_getinfo(hCurlHandle, CURLINFO_HTTP_CODE, &response_code);
+
+ curl_easy_cleanup(hCurlHandle);
+
+ networkAccessOK = (response_code == 206);
+ if (!networkAccessOK) {
+ fprintf(stderr, "network access not working");
+ }
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+static void silent_logger(void *, int, const char *) {}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, basic) {
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ // network access disabled by default
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_log_func(ctx, nullptr, silent_logger);
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_EQ(P, nullptr);
+ proj_context_destroy(ctx);
+
+#ifdef CURL_ENABLED
+ // enable through env variable
+ ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ putenv(const_cast<char *>("PROJ_NETWORK=ON"));
+ P = proj_create(ctx, pipeline);
+ if (networkAccessOK) {
+ ASSERT_NE(P, nullptr);
+ }
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+ putenv(const_cast<char *>("PROJ_NETWORK="));
+#endif
+
+ // still disabled
+ ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_log_func(ctx, nullptr, silent_logger);
+ P = proj_create(ctx, pipeline);
+ ASSERT_EQ(P, nullptr);
+ proj_context_destroy(ctx);
+
+ // enable through API
+ ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ P = proj_create(ctx, pipeline);
+#ifdef CURL_ENABLED
+ if (networkAccessOK) {
+ ASSERT_NE(P, nullptr);
+ } else {
+ ASSERT_EQ(P, nullptr);
+ proj_context_destroy(ctx);
+ return;
+ }
+ double lon = 2;
+ double lat = 49;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, nullptr, 0, 0, nullptr, 0, 0);
+ EXPECT_NEAR(lon, 1.9992776848, 1e-10);
+ EXPECT_NEAR(lat, 48.9999322600, 1e-10);
+
+ proj_destroy(P);
+#else
+ ASSERT_EQ(P, nullptr);
+#endif
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, curl_invalid_resource) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ proj_log_func(ctx, nullptr, silent_logger);
+ auto P = proj_create(
+ ctx, "+proj=hgridshift +grids=https://i_do_not.exist/my.tif");
+ proj_context_destroy(ctx);
+ ASSERT_EQ(P, nullptr);
+}
+#endif
+
+// ---------------------------------------------------------------------------
+
+struct Event {
+ virtual ~Event();
+ std::string type{};
+ PJ_CONTEXT *ctx = nullptr;
+};
+
+Event::~Event() = default;
+
+struct OpenEvent : public Event {
+ OpenEvent() { type = "OpenEvent"; }
+
+ std::string url{};
+ unsigned long long offset = 0;
+ size_t size_to_read = 0;
+ std::vector<unsigned char> response{};
+ std::string errorMsg{};
+ int file_id = 0;
+};
+
+struct CloseEvent : public Event {
+ CloseEvent() { type = "CloseEvent"; }
+
+ int file_id = 0;
+};
+
+struct GetHeaderValueEvent : public Event {
+ GetHeaderValueEvent() { type = "GetHeaderValueEvent"; }
+
+ int file_id = 0;
+ std::string key{};
+ std::string value{};
+};
+
+struct ReadRangeEvent : public Event {
+ ReadRangeEvent() { type = "ReadRangeEvent"; }
+
+ unsigned long long offset = 0;
+ size_t size_to_read = 0;
+ std::vector<unsigned char> response{};
+ std::string errorMsg{};
+ int file_id = 0;
+};
+
+struct File {};
+
+struct ExchangeWithCallback {
+ std::vector<std::unique_ptr<Event>> events{};
+ size_t nextEvent = 0;
+ bool error = false;
+ std::map<int, PROJ_NETWORK_HANDLE *> mapIdToHandle{};
+
+ bool allConsumedAndNoError() const {
+ return nextEvent == events.size() && !error;
+ }
+};
+
+static PROJ_NETWORK_HANDLE *open_cbk(PJ_CONTEXT *ctx, const char *url,
+ unsigned long long offset,
+ size_t size_to_read, void *buffer,
+ size_t *out_size_read,
+ size_t error_string_max_size,
+ char *out_error_string, void *user_data) {
+ auto exchange = static_cast<ExchangeWithCallback *>(user_data);
+ if (exchange->error)
+ return nullptr;
+ if (exchange->nextEvent >= exchange->events.size()) {
+ fprintf(stderr, "unexpected call to open(%s, %ld, %ld)\n", url,
+ (long)offset, (long)size_to_read);
+ exchange->error = true;
+ return nullptr;
+ }
+ auto openEvent =
+ dynamic_cast<OpenEvent *>(exchange->events[exchange->nextEvent].get());
+ if (!openEvent) {
+ fprintf(stderr, "unexpected call to open(%s, %ld, %ld). "
+ "Was expecting a %s event\n",
+ url, (long)offset, (long)size_to_read,
+ exchange->events[exchange->nextEvent]->type.c_str());
+ exchange->error = true;
+ return nullptr;
+ }
+ exchange->nextEvent++;
+ if (openEvent->ctx != ctx || openEvent->url != url ||
+ openEvent->offset != offset ||
+ openEvent->size_to_read != size_to_read) {
+ fprintf(stderr, "wrong call to open(%s, %ld, %ld). Was expecting "
+ "open(%s, %ld, %ld)\n",
+ url, (long)offset, (long)size_to_read, openEvent->url.c_str(),
+ (long)openEvent->offset, (long)openEvent->size_to_read);
+ exchange->error = true;
+ return nullptr;
+ }
+ if (!openEvent->errorMsg.empty()) {
+ snprintf(out_error_string, error_string_max_size, "%s",
+ openEvent->errorMsg.c_str());
+ return nullptr;
+ }
+
+ memcpy(buffer, openEvent->response.data(), openEvent->response.size());
+ *out_size_read = openEvent->response.size();
+ auto handle = reinterpret_cast<PROJ_NETWORK_HANDLE *>(new File());
+ exchange->mapIdToHandle[openEvent->file_id] = handle;
+ return handle;
+}
+
+static void close_cbk(PJ_CONTEXT *ctx, PROJ_NETWORK_HANDLE *handle,
+ void *user_data) {
+ auto exchange = static_cast<ExchangeWithCallback *>(user_data);
+ if (exchange->error)
+ return;
+ if (exchange->nextEvent >= exchange->events.size()) {
+ fprintf(stderr, "unexpected call to close()\n");
+ exchange->error = true;
+ return;
+ }
+ auto closeEvent =
+ dynamic_cast<CloseEvent *>(exchange->events[exchange->nextEvent].get());
+ if (!closeEvent) {
+ fprintf(stderr, "unexpected call to close(). "
+ "Was expecting a %s event\n",
+ exchange->events[exchange->nextEvent]->type.c_str());
+ exchange->error = true;
+ return;
+ }
+ if (closeEvent->ctx != ctx) {
+ fprintf(stderr, "close() called with bad context\n");
+ exchange->error = true;
+ return;
+ }
+ if (exchange->mapIdToHandle[closeEvent->file_id] != handle) {
+ fprintf(stderr, "close() called with bad handle\n");
+ exchange->error = true;
+ return;
+ }
+ exchange->nextEvent++;
+ delete reinterpret_cast<File *>(handle);
+}
+
+static const char *get_header_value_cbk(PJ_CONTEXT *ctx,
+ PROJ_NETWORK_HANDLE *handle,
+ const char *header_name,
+ void *user_data) {
+ auto exchange = static_cast<ExchangeWithCallback *>(user_data);
+ if (exchange->error)
+ return nullptr;
+ if (exchange->nextEvent >= exchange->events.size()) {
+ fprintf(stderr, "unexpected call to get_header_value()\n");
+ exchange->error = true;
+ return nullptr;
+ }
+ auto getHeaderValueEvent = dynamic_cast<GetHeaderValueEvent *>(
+ exchange->events[exchange->nextEvent].get());
+ if (!getHeaderValueEvent) {
+ fprintf(stderr, "unexpected call to get_header_value(). "
+ "Was expecting a %s event\n",
+ exchange->events[exchange->nextEvent]->type.c_str());
+ exchange->error = true;
+ return nullptr;
+ }
+ if (getHeaderValueEvent->ctx != ctx) {
+ fprintf(stderr, "get_header_value() called with bad context\n");
+ exchange->error = true;
+ return nullptr;
+ }
+ if (getHeaderValueEvent->key != header_name) {
+ fprintf(stderr, "wrong call to get_header_value(%s). Was expecting "
+ "get_header_value(%s)\n",
+ header_name, getHeaderValueEvent->key.c_str());
+ exchange->error = true;
+ return nullptr;
+ }
+ if (exchange->mapIdToHandle[getHeaderValueEvent->file_id] != handle) {
+ fprintf(stderr, "get_header_value() called with bad handle\n");
+ exchange->error = true;
+ return nullptr;
+ }
+ exchange->nextEvent++;
+ return getHeaderValueEvent->value.c_str();
+}
+
+static size_t read_range_cbk(PJ_CONTEXT *ctx, PROJ_NETWORK_HANDLE *handle,
+ unsigned long long offset, size_t size_to_read,
+ void *buffer, size_t error_string_max_size,
+ char *out_error_string, void *user_data) {
+ auto exchange = static_cast<ExchangeWithCallback *>(user_data);
+ if (exchange->error)
+ return 0;
+ if (exchange->nextEvent >= exchange->events.size()) {
+ fprintf(stderr, "unexpected call to read_range(%ld, %ld)\n",
+ (long)offset, (long)size_to_read);
+ exchange->error = true;
+ return 0;
+ }
+ auto readRangeEvent = dynamic_cast<ReadRangeEvent *>(
+ exchange->events[exchange->nextEvent].get());
+ if (!readRangeEvent) {
+ fprintf(stderr, "unexpected call to read_range(). "
+ "Was expecting a %s event\n",
+ exchange->events[exchange->nextEvent]->type.c_str());
+ exchange->error = true;
+ return 0;
+ }
+ if (exchange->mapIdToHandle[readRangeEvent->file_id] != handle) {
+ fprintf(stderr, "read_range() called with bad handle\n");
+ exchange->error = true;
+ return 0;
+ }
+ if (readRangeEvent->ctx != ctx || readRangeEvent->offset != offset ||
+ readRangeEvent->size_to_read != size_to_read) {
+ fprintf(stderr, "wrong call to read_range(%ld, %ld). Was expecting "
+ "read_range(%ld, %ld)\n",
+ (long)offset, (long)size_to_read, (long)readRangeEvent->offset,
+ (long)readRangeEvent->size_to_read);
+ exchange->error = true;
+ return 0;
+ }
+ exchange->nextEvent++;
+ if (!readRangeEvent->errorMsg.empty()) {
+ snprintf(out_error_string, error_string_max_size, "%s",
+ readRangeEvent->errorMsg.c_str());
+ return 0;
+ }
+ memcpy(buffer, readRangeEvent->response.data(),
+ readRangeEvent->response.size());
+ return readRangeEvent->response.size();
+}
+
+TEST(networking, custom) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ ExchangeWithCallback exchange;
+ ASSERT_TRUE(proj_context_set_network_callbacks(ctx, open_cbk, close_cbk,
+ get_header_value_cbk,
+ read_range_cbk, &exchange));
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/my.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->response.resize(16384);
+ event->file_id = 1;
+
+ const char *proj_source_data = getenv("PROJ_SOURCE_DATA");
+ ASSERT_TRUE(proj_source_data != nullptr);
+ std::string filename(proj_source_data);
+ filename += "/tests/egm96_15_uncompressed_truncated.tif";
+ FILE *f = fopen(filename.c_str(), "rb");
+ ASSERT_TRUE(f != nullptr);
+ ASSERT_EQ(fread(&event->response[0], 1, 956, f), 956U);
+ fclose(f);
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ auto P = proj_create(
+ ctx, "+proj=vgridshift +grids=https://foo/my.tif +multiplier=1");
+
+ ASSERT_NE(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/my.tif";
+ event->offset = 524288;
+ event->size_to_read = 278528;
+ event->response.resize(278528);
+ event->file_id = 2;
+ float f = 1.25;
+ for (size_t i = 0; i < 278528 / sizeof(float); i++) {
+ memcpy(&event->response[i * sizeof(float)], &f, sizeof(float));
+ }
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = 49 / 180. * M_PI;
+ double z = 0;
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, 1.25);
+ }
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<ReadRangeEvent> event(new ReadRangeEvent());
+ event->ctx = ctx;
+ event->offset = 3670016;
+ event->size_to_read = 278528;
+ event->response.resize(278528);
+ event->file_id = 2;
+ float f = 2.25;
+ for (size_t i = 0; i < 278528 / sizeof(float); i++) {
+ memcpy(&event->response[i * sizeof(float)], &f, sizeof(float));
+ }
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = -49 / 180. * M_PI;
+ double z = 0;
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, 2.25);
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ proj_destroy(P);
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ // Once again ! No network access
+
+ P = proj_create(ctx,
+ "+proj=vgridshift +grids=https://foo/my.tif +multiplier=1");
+ ASSERT_NE(P, nullptr);
+
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = 49 / 180. * M_PI;
+ double z = 0;
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, 1.25);
+ }
+
+ proj_destroy(P);
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, getfilesize) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ ExchangeWithCallback exchange;
+ ASSERT_TRUE(proj_context_set_network_callbacks(ctx, open_cbk, close_cbk,
+ get_header_value_cbk,
+ read_range_cbk, &exchange));
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/getfilesize.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->response.resize(16384);
+ event->file_id = 1;
+
+ const char *proj_source_data = getenv("PROJ_SOURCE_DATA");
+ ASSERT_TRUE(proj_source_data != nullptr);
+ std::string filename(proj_source_data);
+ filename += "/tests/test_vgrid_single_strip_truncated.tif";
+ FILE *f = fopen(filename.c_str(), "rb");
+ ASSERT_TRUE(f != nullptr);
+ ASSERT_EQ(fread(&event->response[0], 1, 550, f), 550U);
+ fclose(f);
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes 0-16383/4153510";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ auto P = proj_create(
+ ctx,
+ "+proj=vgridshift +grids=https://foo/getfilesize.tif +multiplier=1");
+
+ ASSERT_NE(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_destroy(P);
+
+ P = proj_create(
+ ctx,
+ "+proj=vgridshift +grids=https://foo/getfilesize.tif +multiplier=1");
+
+ ASSERT_NE(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_destroy(P);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, simul_open_error) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_log_func(ctx, nullptr, silent_logger);
+ proj_context_set_enable_network(ctx, true);
+ ExchangeWithCallback exchange;
+ ASSERT_TRUE(proj_context_set_network_callbacks(ctx, open_cbk, close_cbk,
+ get_header_value_cbk,
+ read_range_cbk, &exchange));
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/open_error.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->errorMsg = "Cannot open file";
+ event->file_id = 1;
+
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ auto P = proj_create(
+ ctx,
+ "+proj=vgridshift +grids=https://foo/open_error.tif +multiplier=1");
+
+ ASSERT_EQ(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, simul_read_range_error) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ ExchangeWithCallback exchange;
+ ASSERT_TRUE(proj_context_set_network_callbacks(ctx, open_cbk, close_cbk,
+ get_header_value_cbk,
+ read_range_cbk, &exchange));
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/read_range_error.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->response.resize(16384);
+ event->file_id = 1;
+
+ const char *proj_source_data = getenv("PROJ_SOURCE_DATA");
+ ASSERT_TRUE(proj_source_data != nullptr);
+ std::string filename(proj_source_data);
+ filename += "/tests/egm96_15_uncompressed_truncated.tif";
+ FILE *f = fopen(filename.c_str(), "rb");
+ ASSERT_TRUE(f != nullptr);
+ ASSERT_EQ(fread(&event->response[0], 1, 956, f), 956U);
+ fclose(f);
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ auto P = proj_create(ctx, "+proj=vgridshift "
+ "+grids=https://foo/read_range_error.tif "
+ "+multiplier=1");
+
+ ASSERT_NE(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/read_range_error.tif";
+ event->offset = 524288;
+ event->size_to_read = 278528;
+ event->response.resize(278528);
+ event->file_id = 2;
+ float f = 1.25;
+ for (size_t i = 0; i < 278528 / sizeof(float); i++) {
+ memcpy(&event->response[i * sizeof(float)], &f, sizeof(float));
+ }
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = 49 / 180. * M_PI;
+ double z = 0;
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, 1.25);
+ }
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<ReadRangeEvent> event(new ReadRangeEvent());
+ event->ctx = ctx;
+ event->offset = 3670016;
+ event->size_to_read = 278528;
+ event->errorMsg = "read range error";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = -49 / 180. * M_PI;
+ double z = 0;
+ proj_log_func(ctx, nullptr, silent_logger);
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, HUGE_VAL);
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ proj_destroy(P);
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, simul_file_change_while_opened) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ ExchangeWithCallback exchange;
+ ASSERT_TRUE(proj_context_set_network_callbacks(ctx, open_cbk, close_cbk,
+ get_header_value_cbk,
+ read_range_cbk, &exchange));
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/file_change_while_opened.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->response.resize(16384);
+ event->file_id = 1;
+
+ const char *proj_source_data = getenv("PROJ_SOURCE_DATA");
+ ASSERT_TRUE(proj_source_data != nullptr);
+ std::string filename(proj_source_data);
+ filename += "/tests/egm96_15_uncompressed_truncated.tif";
+ FILE *f = fopen(filename.c_str(), "rb");
+ ASSERT_TRUE(f != nullptr);
+ ASSERT_EQ(fread(&event->response[0], 1, 956, f), 956U);
+ fclose(f);
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 1;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ auto P = proj_create(ctx, "+proj=vgridshift "
+ "+grids=https://foo/file_change_while_opened.tif "
+ "+multiplier=1");
+
+ ASSERT_NE(P, nullptr);
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/file_change_while_opened.tif";
+ event->offset = 524288;
+ event->size_to_read = 278528;
+ event->response.resize(278528);
+ event->file_id = 2;
+ float f = 1.25;
+ for (size_t i = 0; i < 278528 / sizeof(float); i++) {
+ memcpy(&event->response[i * sizeof(float)], &f, sizeof(float));
+ }
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date CHANGED!!!!";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 2;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<OpenEvent> event(new OpenEvent());
+ event->ctx = ctx;
+ event->url = "https://foo/file_change_while_opened.tif";
+ event->offset = 0;
+ event->size_to_read = 16384;
+ event->response.resize(16384);
+ event->file_id = 3;
+
+ const char *proj_source_data = getenv("PROJ_SOURCE_DATA");
+ ASSERT_TRUE(proj_source_data != nullptr);
+ std::string filename(proj_source_data);
+ filename += "/tests/egm96_15_uncompressed_truncated.tif";
+ FILE *f = fopen(filename.c_str(), "rb");
+ ASSERT_TRUE(f != nullptr);
+ ASSERT_EQ(fread(&event->response[0], 1, 956, f), 956U);
+ fclose(f);
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Content-Range";
+ event->value = "bytes=0-16383/10000000";
+ event->file_id = 3;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "Last-Modified";
+ event->value = "some_date CHANGED!!!!";
+ event->file_id = 3;
+ exchange.events.emplace_back(std::move(event));
+ }
+ {
+ std::unique_ptr<GetHeaderValueEvent> event(new GetHeaderValueEvent());
+ event->ctx = ctx;
+ event->key = "ETag";
+ event->value = "some_etag";
+ event->file_id = 3;
+ exchange.events.emplace_back(std::move(event));
+ }
+
+ {
+ double lon = 2 / 180. * M_PI;
+ double lat = 49 / 180. * M_PI;
+ double z = 0;
+ ASSERT_EQ(proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat,
+ sizeof(double), 1, &z, sizeof(double), 1,
+ nullptr, 0, 0),
+ 1U);
+ EXPECT_EQ(z, 1.25);
+ }
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ {
+ std::unique_ptr<CloseEvent> event(new CloseEvent());
+ event->ctx = ctx;
+ event->file_id = 3;
+ exchange.events.emplace_back(std::move(event));
+ }
+ proj_destroy(P);
+
+ ASSERT_TRUE(exchange.allConsumedAndNoError());
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, curl_hgridshift) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+
+ // NAD83 to NAD83(HARN) in West-Virginia. Using wvhpgn.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269", "EPSG:4152", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+
+ proj_assign_context(P, ctx); // (dummy) test context reassignment
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_NEAR(c.xyz.x, 39.99999839, 1e-8);
+ EXPECT_NEAR(c.xyz.y, -79.99999807, 1e-8);
+ EXPECT_NEAR(c.xyz.z, 0, 1e-2);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, curl_vgridshift) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+
+ // WGS84 to EGM2008 height. Using egm08_25.tif
+ auto P =
+ proj_create_crs_to_crs(ctx, "EPSG:4326", "EPSG:4326+3855", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = -30; // lat
+ c.xyz.y = 150; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+
+ proj_assign_context(P, ctx); // (dummy) test context reassignment
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_NEAR(c.xyz.x, -30, 1e-8);
+ EXPECT_NEAR(c.xyz.y, 150, 1e-8);
+ EXPECT_NEAR(c.xyz.z, -31.89, 1e-2);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, curl_vgridshift_vertcon) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+
+ // NGVD29 to NAVD88 height. Using vertcone.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269+7968", "EPSG:4269+5703",
+ nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_NEAR(c.xyz.x, 40, 1e-8);
+ EXPECT_NEAR(c.xyz.y, -80, 1e-8);
+ EXPECT_NEAR(c.xyz.z, -0.15, 1e-2);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, network_endpoint_env_variable) {
+ putenv(const_cast<char *>("PROJ_NETWORK_ENDPOINT=http://0.0.0.0/"));
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+
+ // NAD83 to NAD83(HARN) in West-Virginia. Using wvhpgn.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269", "EPSG:4152", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+ putenv(const_cast<char *>("PROJ_NETWORK_ENDPOINT="));
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_EQ(c.xyz.x, HUGE_VAL);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+TEST(networking, network_endpoint_api) {
+ auto ctx = proj_context_create();
+ proj_grid_cache_set_enable(ctx, false);
+ proj_context_set_enable_network(ctx, true);
+ proj_context_set_url_endpoint(ctx, "http://0.0.0.0");
+
+ // NAD83 to NAD83(HARN) in West-Virginia. Using wvhpgn.tif
+ auto P = proj_create_crs_to_crs(ctx, "EPSG:4269", "EPSG:4152", nullptr);
+ ASSERT_NE(P, nullptr);
+
+ PJ_COORD c;
+ c.xyz.x = 40; // lat
+ c.xyz.y = -80; // lon
+ c.xyz.z = 0;
+ c = proj_trans(P, PJ_FWD, c);
+
+ proj_destroy(P);
+ proj_context_destroy(ctx);
+
+ EXPECT_EQ(c.xyz.x, HUGE_VAL);
+}
+
+#endif
+
+// ---------------------------------------------------------------------------
+
+#ifdef CURL_ENABLED
+
+static PROJ_NETWORK_HANDLE *dummy_open_cbk(PJ_CONTEXT *, const char *,
+ unsigned long long, size_t, void *,
+ size_t *, size_t, char *, void *) {
+ assert(false);
+ return nullptr;
+}
+
+static void dummy_close_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *, void *) {
+ assert(false);
+}
+
+static const char *dummy_get_header_value_cbk(PJ_CONTEXT *,
+ PROJ_NETWORK_HANDLE *,
+ const char *, void *) {
+ assert(false);
+ return nullptr;
+}
+
+static size_t dummy_read_range_cbk(PJ_CONTEXT *, PROJ_NETWORK_HANDLE *,
+ unsigned long long, size_t, void *, size_t,
+ char *, void *) {
+ assert(false);
+ return 0;
+}
+
+TEST(networking, cache_basic) {
+ if (!networkAccessOK) {
+ return;
+ }
+
+ proj_cleanup();
+
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+ proj_destroy(P);
+
+ EXPECT_TRUE(!pj_context_get_grid_cache_filename(ctx).empty());
+
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(), &hDB,
+ SQLITE_OPEN_READONLY, nullptr);
+ ASSERT_NE(hDB, nullptr);
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(hDB, "SELECT url, offset FROM chunks WHERE id = ("
+ "SELECT chunk_id FROM linked_chunks WHERE id = ("
+ "SELECT head FROM linked_chunks_head_tail))",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ const char *url =
+ reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 0));
+ ASSERT_NE(url, nullptr);
+ ASSERT_EQ(std::string(url), "https://cdn.proj.org/ntf_r93.tif");
+ ASSERT_EQ(sqlite3_column_int64(hStmt, 1), 0);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+
+ proj_cleanup();
+
+ // Check that a second access doesn't trigger any network activity
+ ASSERT_TRUE(proj_context_set_network_callbacks(
+ ctx, dummy_open_cbk, dummy_close_cbk, dummy_get_header_value_cbk,
+ dummy_read_range_cbk, nullptr));
+ P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+ proj_destroy(P);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, proj_grid_cache_clear) {
+ if (!networkAccessOK) {
+ return;
+ }
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ proj_cleanup();
+
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+ proj_grid_cache_set_filename(ctx, "tmp_proj_db_cache.db");
+ EXPECT_EQ(pj_context_get_grid_cache_filename(ctx),
+ std::string("tmp_proj_db_cache.db"));
+
+ proj_grid_cache_clear(ctx);
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+ proj_destroy(P);
+
+ // Check that the file exists
+ {
+ sqlite3 *hDB = nullptr;
+ ASSERT_EQ(
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(),
+ &hDB, SQLITE_OPEN_READONLY, nullptr),
+ SQLITE_OK);
+ sqlite3_close(hDB);
+ }
+
+ proj_grid_cache_clear(ctx);
+
+ // Check that the file no longer exists
+ {
+ sqlite3 *hDB = nullptr;
+ ASSERT_NE(
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(),
+ &hDB, SQLITE_OPEN_READONLY, nullptr),
+ SQLITE_OK);
+ sqlite3_close(hDB);
+ }
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, cache_saturation) {
+ if (!networkAccessOK) {
+ return;
+ }
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ proj_cleanup();
+
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+ proj_grid_cache_set_filename(ctx, "tmp_proj_db_cache.db");
+
+ proj_grid_cache_clear(ctx);
+
+ // Limit to two chunks
+ putenv(const_cast<char *>("PROJ_GRID_CACHE_MAX_SIZE_BYTES=32768"));
+ proj_grid_cache_set_max_size(ctx, 0);
+ putenv(const_cast<char *>("PROJ_GRID_CACHE_MAX_SIZE_BYTES="));
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+
+ double lon = 2;
+ double lat = 49;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, nullptr, 0, 0, nullptr, 0, 0);
+ EXPECT_NEAR(lon, 1.9992776848, 1e-10);
+ EXPECT_NEAR(lat, 48.9999322600, 1e-10);
+
+ proj_destroy(P);
+
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(), &hDB,
+ SQLITE_OPEN_READONLY, nullptr);
+ ASSERT_NE(hDB, nullptr);
+
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(hDB, "SELECT COUNT(*) FROM chunk_data UNION ALL "
+ "SELECT COUNT(*) FROM chunks UNION ALL "
+ "SELECT COUNT(*) FROM linked_chunks",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(hStmt, 0), 2);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(hStmt, 0), 2);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(hStmt, 0), 2);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+
+ proj_grid_cache_clear(ctx);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, cache_ttl) {
+ if (!networkAccessOK) {
+ return;
+ }
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ proj_cleanup();
+
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+ proj_grid_cache_set_filename(ctx, "tmp_proj_db_cache.db");
+
+ proj_grid_cache_clear(ctx);
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+
+ double lon = 2;
+ double lat = 49;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, nullptr, 0, 0, nullptr, 0, 0);
+ EXPECT_NEAR(lon, 1.9992776848, 1e-10);
+ EXPECT_NEAR(lat, 48.9999322600, 1e-10);
+
+ proj_destroy(P);
+
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(), &hDB,
+ SQLITE_OPEN_READWRITE, nullptr);
+ ASSERT_NE(hDB, nullptr);
+
+ // Force lastChecked to the Epoch so that data is expired.
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(hDB, "UPDATE properties SET lastChecked = 0, "
+ "lastModified = 'foo', etag = 'bar'",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_DONE);
+ sqlite3_finalize(hStmt);
+
+ // Put junk in already cached data to check that we will refresh it.
+ hStmt = nullptr;
+ sqlite3_prepare_v2(hDB, "UPDATE chunk_data SET data = zeroblob(16384)", -1,
+ &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_DONE);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+
+ proj_cleanup();
+
+ // Set a never expire ttl
+ proj_grid_cache_set_ttl(ctx, -1);
+
+ // We'll get junk data, hence the pipeline initialization fails
+ proj_log_func(ctx, nullptr, silent_logger);
+ P = proj_create(ctx, pipeline);
+ ASSERT_EQ(P, nullptr);
+ proj_destroy(P);
+
+ proj_cleanup();
+
+ // Set a normal ttl
+ proj_grid_cache_set_ttl(ctx, 86400);
+
+ // Pipeline creation succeeds
+ P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+ proj_destroy(P);
+
+ hDB = nullptr;
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(), &hDB,
+ SQLITE_OPEN_READWRITE, nullptr);
+ ASSERT_NE(hDB, nullptr);
+ hStmt = nullptr;
+ sqlite3_prepare_v2(hDB,
+ "SELECT lastChecked, lastModified, etag FROM properties",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_NE(sqlite3_column_int64(hStmt, 0), 0);
+ ASSERT_NE(sqlite3_column_text(hStmt, 1), nullptr);
+ ASSERT_NE(std::string(reinterpret_cast<const char *>(
+ sqlite3_column_text(hStmt, 1))),
+ "foo");
+ ASSERT_NE(sqlite3_column_text(hStmt, 2), nullptr);
+ ASSERT_NE(std::string(reinterpret_cast<const char *>(
+ sqlite3_column_text(hStmt, 2))),
+ "bar");
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+
+ proj_grid_cache_clear(ctx);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, cache_lock) {
+ if (!networkAccessOK) {
+ return;
+ }
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=hgridshift +grids=https://cdn.proj.org/ntf_r93.tif "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ proj_cleanup();
+
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+ proj_grid_cache_set_filename(ctx, "tmp_proj_db_cache.db");
+
+ proj_grid_cache_clear(ctx);
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+
+ double lon = 2;
+ double lat = 49;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, nullptr, 0, 0, nullptr, 0, 0);
+ EXPECT_NEAR(lon, 1.9992776848, 1e-10);
+ EXPECT_NEAR(lat, 48.9999322600, 1e-10);
+
+ proj_destroy(P);
+
+ // Take a lock
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2(pj_context_get_grid_cache_filename(ctx).c_str(), &hDB,
+ SQLITE_OPEN_READWRITE, nullptr);
+ ASSERT_NE(hDB, nullptr);
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(hDB, "BEGIN EXCLUSIVE", -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_DONE);
+ sqlite3_finalize(hStmt);
+
+ proj_cleanup();
+
+ time_t start;
+ time(&start);
+ // 2 lock attempts, so we must sleep for each at least 0.5 ms
+ putenv(const_cast<char *>("PROJ_LOCK_MAX_ITERS=25"));
+ P = proj_create(ctx, pipeline);
+ putenv(const_cast<char *>("PROJ_LOCK_MAX_ITERS="));
+ ASSERT_NE(P, nullptr);
+ proj_destroy(P);
+
+ // Check that we have spend more than 1 sec
+ time_t end;
+ time(&end);
+ ASSERT_GE(end - start, 1U);
+
+ sqlite3_close(hDB);
+
+ proj_grid_cache_clear(ctx);
+
+ proj_context_destroy(ctx);
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, download_whole_files) {
+ if (!networkAccessOK) {
+ return;
+ }
+
+ proj_cleanup();
+ unlink("proj_test_tmp/cache.db");
+ unlink("proj_test_tmp/dvr90.tif");
+ rmdir("proj_test_tmp");
+
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY="));
+ putenv(const_cast<char *>("PROJ_USER_WRITABLE_DIRECTORY=./proj_test_tmp"));
+ putenv(const_cast<char *>("PROJ_FULL_FILE_CHUNK_SIZE=100000"));
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+
+ ASSERT_TRUE(proj_is_download_needed(ctx, "dvr90.gtx", false));
+
+ ASSERT_TRUE(proj_download_file(ctx, "dvr90.gtx", false, nullptr, nullptr));
+
+ FILE *f = fopen("proj_test_tmp/dvr90.tif", "rb");
+ ASSERT_NE(f, nullptr);
+ fclose(f);
+
+ proj_context_set_enable_network(ctx, false);
+
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=vgridshift +grids=dvr90.gtx +multiplier=1 "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+
+ double lon = 12;
+ double lat = 56;
+ double z = 0;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, &z, sizeof(double), 1, nullptr, 0, 0);
+ EXPECT_NEAR(z, 36.5909996032715, 1e-10);
+ proj_destroy(P);
+
+ proj_context_set_enable_network(ctx, true);
+
+ ASSERT_FALSE(proj_is_download_needed(ctx, "dvr90.gtx", false));
+
+ {
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2("proj_test_tmp/cache.db", &hDB, SQLITE_OPEN_READWRITE,
+ nullptr);
+ ASSERT_NE(hDB, nullptr);
+ // Force lastChecked to the Epoch so that data is expired.
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(
+ hDB, "UPDATE downloaded_file_properties SET lastChecked = 0", -1,
+ &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_DONE);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+ }
+
+ // If we ignore TTL settings, then no network access will be done
+ ASSERT_FALSE(proj_is_download_needed(ctx, "dvr90.gtx", true));
+
+ {
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2("proj_test_tmp/cache.db", &hDB, SQLITE_OPEN_READWRITE,
+ nullptr);
+ ASSERT_NE(hDB, nullptr);
+ // Check that the lastChecked timestamp is still 0
+ sqlite3_stmt *hStmt = nullptr;
+ sqlite3_prepare_v2(hDB,
+ "SELECT lastChecked FROM downloaded_file_properties",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_EQ(sqlite3_column_int64(hStmt, 0), 0);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+ }
+
+ // Should recheck from the CDN, update last_checked and do nothing
+ ASSERT_FALSE(proj_is_download_needed(ctx, "dvr90.gtx", false));
+
+ {
+ sqlite3 *hDB = nullptr;
+ sqlite3_open_v2("proj_test_tmp/cache.db", &hDB, SQLITE_OPEN_READWRITE,
+ nullptr);
+ ASSERT_NE(hDB, nullptr);
+ sqlite3_stmt *hStmt = nullptr;
+ // Check that the lastChecked timestamp has been updated
+ sqlite3_prepare_v2(hDB,
+ "SELECT lastChecked FROM downloaded_file_properties",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_ROW);
+ ASSERT_NE(sqlite3_column_int64(hStmt, 0), 0);
+ sqlite3_finalize(hStmt);
+ hStmt = nullptr;
+
+ // Now invalid lastModified. This should trigger a new download
+ sqlite3_prepare_v2(
+ hDB, "UPDATE downloaded_file_properties SET lastChecked = 0, "
+ "lastModified = 'foo'",
+ -1, &hStmt, nullptr);
+ ASSERT_NE(hStmt, nullptr);
+ ASSERT_EQ(sqlite3_step(hStmt), SQLITE_DONE);
+ sqlite3_finalize(hStmt);
+ sqlite3_close(hDB);
+ }
+
+ ASSERT_TRUE(proj_is_download_needed(ctx, "dvr90.gtx", false));
+
+ // Redo download with a progress callback this time.
+ unlink("proj_test_tmp/dvr90.tif");
+
+ const auto cbk = [](PJ_CONTEXT *l_ctx, double pct, void *user_data) -> int {
+ auto vect = static_cast<std::vector<std::pair<PJ_CONTEXT *, double>> *>(
+ user_data);
+ vect->push_back(std::pair<PJ_CONTEXT *, double>(l_ctx, pct));
+ return true;
+ };
+
+ std::vector<std::pair<PJ_CONTEXT *, double>> vectPct;
+ ASSERT_TRUE(proj_download_file(ctx, "dvr90.gtx", false, cbk, &vectPct));
+ ASSERT_EQ(vectPct.size(), 3U);
+ ASSERT_EQ(vectPct.back().first, ctx);
+ ASSERT_EQ(vectPct.back().second, 1.0);
+
+ proj_context_destroy(ctx);
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES"));
+ putenv(const_cast<char *>("PROJ_USER_WRITABLE_DIRECTORY="));
+ putenv(const_cast<char *>("PROJ_FULL_FILE_CHUNK_SIZE="));
+ unlink("proj_test_tmp/cache.db");
+ unlink("proj_test_tmp/dvr90.tif");
+ rmdir("proj_test_tmp");
+}
+
+// ---------------------------------------------------------------------------
+
+TEST(networking, file_api) {
+ if (!networkAccessOK) {
+ return;
+ }
+
+ proj_cleanup();
+ unlink("proj_test_tmp/cache.db");
+ unlink("proj_test_tmp/dvr90.tif");
+ rmdir("proj_test_tmp");
+
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY="));
+ putenv(const_cast<char *>("PROJ_USER_WRITABLE_DIRECTORY=./proj_test_tmp"));
+ putenv(const_cast<char *>("PROJ_FULL_FILE_CHUNK_SIZE=30000"));
+ auto ctx = proj_context_create();
+ proj_context_set_enable_network(ctx, true);
+
+ struct UserData {
+ bool in_open = false;
+ bool in_read = false;
+ bool in_write = false;
+ bool in_seek = false;
+ bool in_tell = false;
+ bool in_close = false;
+ bool in_exists = false;
+ bool in_mkdir = false;
+ bool in_unlink = false;
+ bool in_rename = false;
+ };
+
+ struct PROJ_FILE_API api;
+ api.version = 1;
+ api.open_cbk = [](PJ_CONTEXT *, const char *filename,
+ PROJ_OPEN_ACCESS access,
+ void *user_data) -> PROJ_FILE_HANDLE * {
+ static_cast<UserData *>(user_data)->in_open = true;
+ return reinterpret_cast<PROJ_FILE_HANDLE *>(fopen(
+ filename,
+ access == PROJ_OPEN_ACCESS_READ_ONLY
+ ? "rb"
+ : access == PROJ_OPEN_ACCESS_READ_UPDATE ? "r+b" : "w+b"));
+ };
+ api.read_cbk = [](PJ_CONTEXT *, PROJ_FILE_HANDLE *handle, void *buffer,
+ size_t sizeBytes, void *user_data) -> size_t {
+ static_cast<UserData *>(user_data)->in_read = true;
+ return fread(buffer, 1, sizeBytes, reinterpret_cast<FILE *>(handle));
+ };
+ api.write_cbk = [](PJ_CONTEXT *, PROJ_FILE_HANDLE *handle,
+ const void *buffer, size_t sizeBytes,
+ void *user_data) -> size_t {
+ static_cast<UserData *>(user_data)->in_write = true;
+ return fwrite(buffer, 1, sizeBytes, reinterpret_cast<FILE *>(handle));
+ };
+ api.seek_cbk = [](PJ_CONTEXT *, PROJ_FILE_HANDLE *handle, long long offset,
+ int whence, void *user_data) -> int {
+ static_cast<UserData *>(user_data)->in_seek = true;
+ return fseek(reinterpret_cast<FILE *>(handle),
+ static_cast<long>(offset), whence) == 0;
+ };
+ api.tell_cbk = [](PJ_CONTEXT *, PROJ_FILE_HANDLE *handle,
+ void *user_data) -> unsigned long long {
+ static_cast<UserData *>(user_data)->in_tell = true;
+ return ftell(reinterpret_cast<FILE *>(handle));
+ };
+ api.close_cbk = [](PJ_CONTEXT *, PROJ_FILE_HANDLE *handle,
+ void *user_data) -> void {
+ static_cast<UserData *>(user_data)->in_close = true;
+ fclose(reinterpret_cast<FILE *>(handle));
+ };
+ api.exists_cbk = [](PJ_CONTEXT *, const char *filename,
+ void *user_data) -> int {
+ static_cast<UserData *>(user_data)->in_exists = true;
+ struct stat buf;
+ return stat(filename, &buf) == 0;
+ };
+ api.mkdir_cbk = [](PJ_CONTEXT *, const char *filename,
+ void *user_data) -> int {
+ static_cast<UserData *>(user_data)->in_mkdir = true;
+#ifdef _WIN32
+ return mkdir(filename) == 0;
+#else
+ return mkdir(filename, 0755) == 0;
+#endif
+ };
+ api.unlink_cbk = [](PJ_CONTEXT *, const char *filename,
+ void *user_data) -> int {
+ static_cast<UserData *>(user_data)->in_unlink = true;
+ return unlink(filename) == 0;
+ };
+ api.rename_cbk = [](PJ_CONTEXT *, const char *oldPath, const char *newPath,
+ void *user_data) -> int {
+ static_cast<UserData *>(user_data)->in_rename = true;
+ return rename(oldPath, newPath) == 0;
+ };
+
+ UserData userData;
+ ASSERT_TRUE(proj_context_set_fileapi(ctx, &api, &userData));
+
+ ASSERT_TRUE(proj_is_download_needed(ctx, "dvr90.gtx", false));
+
+ ASSERT_TRUE(proj_download_file(ctx, "dvr90.gtx", false, nullptr, nullptr));
+
+ ASSERT_TRUE(userData.in_open);
+ ASSERT_FALSE(userData.in_read);
+ ASSERT_TRUE(userData.in_write);
+ ASSERT_TRUE(userData.in_close);
+ ASSERT_TRUE(userData.in_exists);
+ ASSERT_TRUE(userData.in_mkdir);
+ ASSERT_TRUE(userData.in_unlink);
+ ASSERT_TRUE(userData.in_rename);
+
+ proj_context_set_enable_network(ctx, false);
+
+ const char *pipeline =
+ "+proj=pipeline "
+ "+step +proj=unitconvert +xy_in=deg +xy_out=rad "
+ "+step +proj=vgridshift +grids=dvr90.gtx +multiplier=1 "
+ "+step +proj=unitconvert +xy_in=rad +xy_out=deg";
+
+ auto P = proj_create(ctx, pipeline);
+ ASSERT_NE(P, nullptr);
+
+ double lon = 12;
+ double lat = 56;
+ double z = 0;
+ proj_trans_generic(P, PJ_FWD, &lon, sizeof(double), 1, &lat, sizeof(double),
+ 1, &z, sizeof(double), 1, nullptr, 0, 0);
+ EXPECT_NEAR(z, 36.5909996032715, 1e-10);
+
+ proj_destroy(P);
+
+ ASSERT_TRUE(userData.in_read);
+ ASSERT_TRUE(userData.in_seek);
+
+ proj_context_destroy(ctx);
+ putenv(const_cast<char *>("PROJ_SKIP_READ_USER_WRITABLE_DIRECTORY=YES"));
+ putenv(const_cast<char *>("PROJ_USER_WRITABLE_DIRECTORY="));
+ putenv(const_cast<char *>("PROJ_FULL_FILE_CHUNK_SIZE="));
+ unlink("proj_test_tmp/cache.db");
+ unlink("proj_test_tmp/dvr90.tif");
+ rmdir("proj_test_tmp");
+}
+
+#endif
+
+} // namespace
diff --git a/test/unit/test_operation.cpp b/test/unit/test_operation.cpp
index b2c13b6c..8ee5814b 100644
--- a/test/unit/test_operation.cpp
+++ b/test/unit/test_operation.cpp
@@ -4832,7 +4832,7 @@ TEST(operation, geogCRS_to_geogCRS_context_concatenated_operation) {
EXPECT_TRUE(nn_dynamic_pointer_cast<ConcatenatedOperation>(list[0]) !=
nullptr);
- auto grids = list[0]->gridsNeeded(DatabaseContext::create());
+ auto grids = list[0]->gridsNeeded(DatabaseContext::create(), false);
EXPECT_EQ(grids.size(), 1U);
}
@@ -6508,7 +6508,7 @@ TEST(operation, transformation_height_to_PROJ_string) {
EXPECT_EQ(transf->exportToPROJString(PROJStringFormatter::create().get()),
"+proj=vgridshift +grids=egm08_25.gtx +multiplier=1");
- auto grids = transf->gridsNeeded(DatabaseContext::create());
+ auto grids = transf->gridsNeeded(DatabaseContext::create(), false);
ASSERT_EQ(grids.size(), 1U);
auto gridDesc = *(grids.begin());
EXPECT_EQ(gridDesc.shortName, "egm08_25.gtx");
@@ -6820,7 +6820,7 @@ TEST(operation, compoundCRS_with_boundGeogCRS_and_boundVerticalCRS_to_geogCRS) {
"+step +proj=unitconvert +xy_in=rad +xy_out=deg "
"+step +proj=axisswap +order=2,1");
- auto grids = op->gridsNeeded(DatabaseContext::create());
+ auto grids = op->gridsNeeded(DatabaseContext::create(), false);
EXPECT_EQ(grids.size(), 1U);
auto opInverse = CoordinateOperationFactory::create()->createOperation(
@@ -8262,8 +8262,8 @@ TEST(operation, isPROJInstantiable) {
auto transformation = Transformation::createGeocentricTranslations(
PropertyMap(), GeographicCRS::EPSG_4269, GeographicCRS::EPSG_4326,
1.0, 2.0, 3.0, {});
- EXPECT_TRUE(
- transformation->isPROJInstantiable(DatabaseContext::create()));
+ EXPECT_TRUE(transformation->isPROJInstantiable(
+ DatabaseContext::create(), false));
}
// Missing grid
@@ -8271,8 +8271,8 @@ TEST(operation, isPROJInstantiable) {
auto transformation = Transformation::createNTv2(
PropertyMap(), GeographicCRS::EPSG_4807, GeographicCRS::EPSG_4326,
"foo.gsb", std::vector<PositionalAccuracyNNPtr>());
- EXPECT_FALSE(
- transformation->isPROJInstantiable(DatabaseContext::create()));
+ EXPECT_FALSE(transformation->isPROJInstantiable(
+ DatabaseContext::create(), false));
}
// Unsupported method
@@ -8283,8 +8283,8 @@ TEST(operation, isPROJInstantiable) {
PropertyMap(), std::vector<OperationParameterNNPtr>{}),
std::vector<GeneralParameterValueNNPtr>{},
std::vector<PositionalAccuracyNNPtr>{});
- EXPECT_FALSE(
- transformation->isPROJInstantiable(DatabaseContext::create()));
+ EXPECT_FALSE(transformation->isPROJInstantiable(
+ DatabaseContext::create(), false));
}
}