From 610957f7035242f15743c399ffd429b92bc36206 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 18 Dec 2018 20:24:11 +0100 Subject: cpp conversion: minimal steps to fix compilation errors, not warnings --- src/Makefile.am | 106 +-- src/PJ_aea.c | 222 ----- src/PJ_aea.cpp | 222 +++++ src/PJ_aeqd.c | 323 ------- src/PJ_aeqd.cpp | 323 +++++++ src/PJ_affine.c | 246 ----- src/PJ_affine.cpp | 246 +++++ src/PJ_airy.c | 151 ---- src/PJ_airy.cpp | 151 ++++ src/PJ_aitoff.c | 197 ---- src/PJ_aitoff.cpp | 197 ++++ src/PJ_august.c | 34 - src/PJ_august.cpp | 34 + src/PJ_axisswap.c | 300 ------- src/PJ_axisswap.cpp | 300 +++++++ src/PJ_bacon.c | 79 -- src/PJ_bacon.cpp | 79 ++ src/PJ_bertin1953.c | 94 -- src/PJ_bertin1953.cpp | 94 ++ src/PJ_bipc.c | 174 ---- src/PJ_bipc.cpp | 174 ++++ src/PJ_boggs.c | 43 - src/PJ_boggs.cpp | 43 + src/PJ_bonne.c | 134 --- src/PJ_bonne.cpp | 134 +++ src/PJ_calcofi.c | 163 ---- src/PJ_calcofi.cpp | 163 ++++ src/PJ_cart.c | 219 ----- src/PJ_cart.cpp | 219 +++++ src/PJ_cass.c | 121 --- src/PJ_cass.cpp | 121 +++ src/PJ_cc.c | 41 - src/PJ_cc.cpp | 41 + src/PJ_ccon.c | 107 --- src/PJ_ccon.cpp | 107 +++ src/PJ_cea.c | 101 --- src/PJ_cea.cpp | 101 +++ src/PJ_chamb.c | 139 --- src/PJ_chamb.cpp | 139 +++ src/PJ_collg.c | 53 -- src/PJ_collg.cpp | 53 ++ src/PJ_comill.c | 84 -- src/PJ_comill.cpp | 84 ++ src/PJ_crast.c | 40 - src/PJ_crast.cpp | 40 + src/PJ_deformation.c | 323 ------- src/PJ_deformation.cpp | 323 +++++++ src/PJ_denoy.c | 32 - src/PJ_denoy.cpp | 32 + src/PJ_eck1.c | 41 - src/PJ_eck1.cpp | 41 + src/PJ_eck2.c | 56 -- src/PJ_eck2.cpp | 56 ++ src/PJ_eck3.c | 110 --- src/PJ_eck3.cpp | 110 +++ src/PJ_eck4.c | 63 -- src/PJ_eck4.cpp | 63 ++ src/PJ_eck5.c | 40 - src/PJ_eck5.cpp | 40 + src/PJ_eqc.c | 52 -- src/PJ_eqc.cpp | 52 ++ src/PJ_eqdc.c | 120 --- src/PJ_eqdc.cpp | 120 +++ src/PJ_eqearth.c | 162 ---- src/PJ_eqearth.cpp | 162 ++++ src/PJ_fahey.c | 41 - src/PJ_fahey.cpp | 41 + src/PJ_fouc_s.c | 70 -- src/PJ_fouc_s.cpp | 70 ++ src/PJ_gall.c | 44 - src/PJ_gall.cpp | 44 + src/PJ_geoc.c | 59 -- src/PJ_geoc.cpp | 59 ++ src/PJ_geos.c | 236 ----- src/PJ_geos.cpp | 236 +++++ src/PJ_gins8.c | 33 - src/PJ_gins8.cpp | 33 + src/PJ_gn_sinu.c | 187 ---- src/PJ_gn_sinu.cpp | 187 ++++ src/PJ_gnom.c | 143 --- src/PJ_gnom.cpp | 143 +++ src/PJ_goode.c | 82 -- src/PJ_goode.cpp | 82 ++ src/PJ_gstmerc.c | 72 -- src/PJ_gstmerc.cpp | 72 ++ src/PJ_hammer.c | 75 -- src/PJ_hammer.cpp | 75 ++ src/PJ_hatano.c | 83 -- src/PJ_hatano.cpp | 83 ++ src/PJ_healpix.c | 672 -------------- src/PJ_healpix.cpp | 672 ++++++++++++++ src/PJ_helmert.c | 753 ---------------- src/PJ_helmert.cpp | 753 ++++++++++++++++ src/PJ_hgridshift.c | 131 --- src/PJ_hgridshift.cpp | 131 +++ src/PJ_horner.c | 513 ----------- src/PJ_horner.cpp | 513 +++++++++++ src/PJ_igh.c | 225 ----- src/PJ_igh.cpp | 225 +++++ src/PJ_imw_p.c | 213 ----- src/PJ_imw_p.cpp | 213 +++++ src/PJ_isea.c | 1082 ---------------------- src/PJ_isea.cpp | 1082 ++++++++++++++++++++++ src/PJ_krovak.c | 220 ----- src/PJ_krovak.cpp | 220 +++++ src/PJ_labrd.c | 130 --- src/PJ_labrd.cpp | 130 +++ src/PJ_laea.c | 296 ------ src/PJ_laea.cpp | 296 ++++++ src/PJ_lagrng.c | 96 -- src/PJ_lagrng.cpp | 96 ++ src/PJ_larr.c | 28 - src/PJ_larr.cpp | 28 + src/PJ_lask.c | 39 - src/PJ_lask.cpp | 39 + src/PJ_latlong.c | 124 --- src/PJ_latlong.cpp | 124 +++ src/PJ_lcc.c | 128 --- src/PJ_lcc.cpp | 128 +++ src/PJ_lcca.c | 162 ---- src/PJ_lcca.cpp | 162 ++++ src/PJ_loxim.c | 75 -- src/PJ_loxim.cpp | 75 ++ src/PJ_lsat.c | 210 ----- src/PJ_lsat.cpp | 210 +++++ src/PJ_mbt_fps.c | 57 -- src/PJ_mbt_fps.cpp | 57 ++ src/PJ_mbtfpp.c | 65 -- src/PJ_mbtfpp.cpp | 65 ++ src/PJ_mbtfpq.c | 74 -- src/PJ_mbtfpq.cpp | 74 ++ src/PJ_merc.c | 101 --- src/PJ_merc.cpp | 101 +++ src/PJ_mill.c | 37 - src/PJ_mill.cpp | 37 + src/PJ_misrsom.c | 217 ----- src/PJ_misrsom.cpp | 217 +++++ src/PJ_mod_ster.c | 280 ------ src/PJ_mod_ster.cpp | 280 ++++++ src/PJ_moll.c | 110 --- src/PJ_moll.cpp | 110 +++ src/PJ_molodensky.c | 327 ------- src/PJ_molodensky.cpp | 327 +++++++ src/PJ_natearth.c | 100 --- src/PJ_natearth.cpp | 100 +++ src/PJ_natearth2.c | 97 -- src/PJ_natearth2.cpp | 97 ++ src/PJ_nell.c | 51 -- src/PJ_nell.cpp | 51 ++ src/PJ_nell_h.c | 53 -- src/PJ_nell_h.cpp | 53 ++ src/PJ_nocol.c | 54 -- src/PJ_nocol.cpp | 54 ++ src/PJ_nsper.c | 198 ---- src/PJ_nsper.cpp | 198 ++++ src/PJ_nzmg.c | 123 --- src/PJ_nzmg.cpp | 123 +++ src/PJ_ob_tran.c | 243 ----- src/PJ_ob_tran.cpp | 243 +++++ src/PJ_ocea.c | 100 --- src/PJ_ocea.cpp | 100 +++ src/PJ_oea.c | 85 -- src/PJ_oea.cpp | 85 ++ src/PJ_omerc.c | 227 ----- src/PJ_omerc.cpp | 227 +++++ src/PJ_ortho.c | 139 --- src/PJ_ortho.cpp | 139 +++ src/PJ_patterson.c | 117 --- src/PJ_patterson.cpp | 117 +++ src/PJ_pipeline.c | 501 ----------- src/PJ_pipeline.cpp | 501 +++++++++++ src/PJ_poly.c | 169 ---- src/PJ_poly.cpp | 169 ++++ src/PJ_putp2.c | 61 -- src/PJ_putp2.cpp | 61 ++ src/PJ_putp3.c | 65 -- src/PJ_putp3.cpp | 65 ++ src/PJ_putp4p.c | 74 -- src/PJ_putp4p.cpp | 74 ++ src/PJ_putp5.c | 73 -- src/PJ_putp5.cpp | 73 ++ src/PJ_putp6.c | 95 -- src/PJ_putp6.cpp | 95 ++ src/PJ_qsc.c | 402 --------- src/PJ_qsc.cpp | 402 +++++++++ src/PJ_robin.c | 159 ---- src/PJ_robin.cpp | 159 ++++ src/PJ_rpoly.c | 56 -- src/PJ_rpoly.cpp | 56 ++ src/PJ_sch.c | 230 ----- src/PJ_sch.cpp | 230 +++++ src/PJ_sconics.c | 216 ----- src/PJ_sconics.cpp | 216 +++++ src/PJ_somerc.c | 92 -- src/PJ_somerc.cpp | 92 ++ src/PJ_stere.c | 316 ------- src/PJ_stere.cpp | 316 +++++++ src/PJ_sterea.c | 115 --- src/PJ_sterea.cpp | 115 +++ src/PJ_sts.c | 107 --- src/PJ_sts.cpp | 107 +++ src/PJ_tcc.c | 34 - src/PJ_tcc.cpp | 34 + src/PJ_tcea.c | 36 - src/PJ_tcea.cpp | 36 + src/PJ_times.c | 79 -- src/PJ_times.cpp | 79 ++ src/PJ_tmerc.c | 208 ----- src/PJ_tmerc.cpp | 208 +++++ src/PJ_tobmerc.c | 51 -- src/PJ_tobmerc.cpp | 51 ++ src/PJ_tpeqd.c | 107 --- src/PJ_tpeqd.cpp | 107 +++ src/PJ_unitconvert.c | 548 ----------- src/PJ_unitconvert.cpp | 548 +++++++++++ src/PJ_urm5.c | 54 -- src/PJ_urm5.cpp | 54 ++ src/PJ_urmfps.c | 74 -- src/PJ_urmfps.cpp | 74 ++ src/PJ_vandg.c | 106 --- src/PJ_vandg.cpp | 106 +++ src/PJ_vandg2.c | 74 -- src/PJ_vandg2.cpp | 74 ++ src/PJ_vandg4.c | 55 -- src/PJ_vandg4.cpp | 55 ++ src/PJ_vgridshift.c | 142 --- src/PJ_vgridshift.cpp | 142 +++ src/PJ_wag2.c | 38 - src/PJ_wag2.cpp | 38 + src/PJ_wag3.c | 48 - src/PJ_wag3.cpp | 48 + src/PJ_wag7.c | 30 - src/PJ_wag7.cpp | 30 + src/PJ_wink1.c | 44 - src/PJ_wink1.cpp | 44 + src/PJ_wink2.c | 52 -- src/PJ_wink2.cpp | 52 ++ src/aasincos.c | 38 - src/aasincos.cpp | 38 + src/adjlon.c | 20 - src/adjlon.cpp | 20 + src/bch2bps.c | 152 ---- src/bch2bps.cpp | 152 ++++ src/bchgen.c | 58 -- src/bchgen.cpp | 58 ++ src/bin_cct.cmake | 2 +- src/bin_cs2cs.cmake | 4 +- src/bin_geod.cmake | 4 +- src/bin_geodtest.cmake | 2 +- src/bin_gie.cmake | 2 +- src/bin_nad2bin.cmake | 2 +- src/bin_proj.cmake | 6 +- src/biveval.c | 87 -- src/biveval.cpp | 87 ++ src/cct.c | 472 ---------- src/cct.cpp | 472 ++++++++++ src/dmstor.c | 121 --- src/dmstor.cpp | 122 +++ src/emess.c | 68 -- src/emess.cpp | 68 ++ src/gen_cheb.c | 78 -- src/gen_cheb.cpp | 78 ++ src/geocent.c | 436 --------- src/geocent.cpp | 436 +++++++++ src/geod.c | 239 ----- src/geod.cpp | 239 +++++ src/geod_interface.c | 33 - src/geod_interface.cpp | 33 + src/geod_set.c | 75 -- src/geod_set.cpp | 75 ++ src/geodesic.c | 2100 ------------------------------------------- src/geodesic.cpp | 2100 +++++++++++++++++++++++++++++++++++++++++++ src/geodtest.c | 1069 ---------------------- src/geodtest.cpp | 1069 ++++++++++++++++++++++ src/gie.c | 1456 ------------------------------ src/gie.cpp | 1456 ++++++++++++++++++++++++++++++ src/jniproj.c | 472 ---------- src/jniproj.cpp | 472 ++++++++++ src/lib_proj.cmake | 364 ++++---- src/mk_cheby.c | 192 ---- src/mk_cheby.cpp | 192 ++++ src/multistresstest.c | 488 ---------- src/multistresstest.cpp | 488 ++++++++++ src/nad2bin.c | 382 -------- src/nad2bin.cpp | 382 ++++++++ src/nad_cvt.c | 76 -- src/nad_cvt.cpp | 76 ++ src/nad_init.c | 305 ------- src/nad_init.cpp | 305 +++++++ src/nad_intr.c | 68 -- src/nad_intr.cpp | 68 ++ src/p_series.c | 42 - src/p_series.cpp | 42 + src/pj_apply_gridshift.c | 356 -------- src/pj_apply_gridshift.cpp | 356 ++++++++ src/pj_apply_vgridshift.c | 331 ------- src/pj_apply_vgridshift.cpp | 331 +++++++ src/pj_auth.c | 36 - src/pj_auth.cpp | 36 + src/pj_ctx.c | 233 ----- src/pj_ctx.cpp | 233 +++++ src/pj_datum_set.c | 169 ---- src/pj_datum_set.cpp | 169 ++++ src/pj_datums.c | 99 -- src/pj_datums.cpp | 99 ++ src/pj_deriv.c | 70 -- src/pj_deriv.cpp | 70 ++ src/pj_ell_set.c | 727 --------------- src/pj_ell_set.cpp | 727 +++++++++++++++ src/pj_ellps.c | 62 -- src/pj_ellps.cpp | 62 ++ src/pj_errno.c | 17 - src/pj_errno.cpp | 17 + src/pj_factors.c | 107 --- src/pj_factors.cpp | 107 +++ src/pj_fileapi.c | 213 ----- src/pj_fileapi.cpp | 213 +++++ src/pj_fwd.c | 261 ------ src/pj_fwd.cpp | 261 ++++++ src/pj_gauss.c | 101 --- src/pj_gauss.cpp | 101 +++ src/pj_gc_reader.c | 247 ----- src/pj_gc_reader.cpp | 247 +++++ src/pj_geocent.c | 62 -- src/pj_geocent.cpp | 62 ++ src/pj_gridcatalog.c | 295 ------ src/pj_gridcatalog.cpp | 295 ++++++ src/pj_gridinfo.c | 991 -------------------- src/pj_gridinfo.cpp | 991 ++++++++++++++++++++ src/pj_gridlist.c | 219 ----- src/pj_gridlist.cpp | 219 +++++ src/pj_init.c | 870 ------------------ src/pj_init.cpp | 888 ++++++++++++++++++ src/pj_initcache.c | 184 ---- src/pj_initcache.cpp | 184 ++++ src/pj_internal.c | 445 --------- src/pj_internal.cpp | 445 +++++++++ src/pj_inv.c | 238 ----- src/pj_inv.cpp | 238 +++++ src/pj_list.c | 32 - src/pj_list.cpp | 32 + src/pj_log.c | 98 -- src/pj_log.cpp | 98 ++ src/pj_malloc.c | 239 ----- src/pj_malloc.cpp | 240 +++++ src/pj_math.c | 108 --- src/pj_math.cpp | 108 +++ src/pj_mlfn.c | 64 -- src/pj_mlfn.cpp | 64 ++ src/pj_msfn.c | 7 - src/pj_msfn.cpp | 7 + src/pj_mutex.c | 261 ------ src/pj_mutex.cpp | 261 ++++++ src/pj_open_lib.c | 247 ----- src/pj_open_lib.cpp | 247 +++++ src/pj_param.c | 189 ---- src/pj_param.cpp | 189 ++++ src/pj_phi2.c | 46 - src/pj_phi2.cpp | 46 + src/pj_pr_list.c | 104 --- src/pj_pr_list.cpp | 104 +++ src/pj_qsfn.c | 22 - src/pj_qsfn.cpp | 22 + src/pj_release.c | 18 - src/pj_release.cpp | 18 + src/pj_strerrno.c | 109 --- src/pj_strerrno.cpp | 109 +++ src/pj_strtod.c | 195 ---- src/pj_strtod.cpp | 195 ++++ src/pj_transform.c | 1060 ---------------------- src/pj_transform.cpp | 1060 ++++++++++++++++++++++ src/pj_tsfn.c | 16 - src/pj_tsfn.cpp | 16 + src/pj_units.c | 58 -- src/pj_units.cpp | 58 ++ src/pj_utils.c | 181 ---- src/pj_utils.cpp | 181 ++++ src/pj_zpoly1.c | 46 - src/pj_zpoly1.cpp | 46 + src/proj.c | 581 ------------ src/proj.cpp | 581 ++++++++++++ src/proj_4D_api.c | 1281 -------------------------- src/proj_4D_api.cpp | 1285 ++++++++++++++++++++++++++ src/proj_etmerc.c | 360 -------- src/proj_etmerc.cpp | 360 ++++++++ src/proj_mdist.c | 127 --- src/proj_mdist.cpp | 127 +++ src/proj_rouss.c | 156 ---- src/proj_rouss.cpp | 156 ++++ src/proj_strtod.c | 440 --------- src/proj_strtod.cpp | 440 +++++++++ src/projects.h | 7 +- src/rtodms.c | 86 -- src/rtodms.cpp | 86 ++ src/test228.c | 86 -- src/test228.cpp | 86 ++ src/vector1.c | 29 - src/vector1.cpp | 29 + 398 files changed, 39031 insertions(+), 39008 deletions(-) delete mode 100644 src/PJ_aea.c create mode 100644 src/PJ_aea.cpp delete mode 100644 src/PJ_aeqd.c create mode 100644 src/PJ_aeqd.cpp delete mode 100644 src/PJ_affine.c create mode 100644 src/PJ_affine.cpp delete mode 100644 src/PJ_airy.c create mode 100644 src/PJ_airy.cpp delete mode 100644 src/PJ_aitoff.c create mode 100644 src/PJ_aitoff.cpp delete mode 100644 src/PJ_august.c create mode 100644 src/PJ_august.cpp delete mode 100644 src/PJ_axisswap.c create mode 100644 src/PJ_axisswap.cpp delete mode 100644 src/PJ_bacon.c create mode 100644 src/PJ_bacon.cpp delete mode 100644 src/PJ_bertin1953.c create mode 100644 src/PJ_bertin1953.cpp delete mode 100644 src/PJ_bipc.c create mode 100644 src/PJ_bipc.cpp delete mode 100644 src/PJ_boggs.c create mode 100644 src/PJ_boggs.cpp delete mode 100644 src/PJ_bonne.c create mode 100644 src/PJ_bonne.cpp delete mode 100644 src/PJ_calcofi.c create mode 100644 src/PJ_calcofi.cpp delete mode 100644 src/PJ_cart.c create mode 100644 src/PJ_cart.cpp delete mode 100644 src/PJ_cass.c create mode 100644 src/PJ_cass.cpp delete mode 100644 src/PJ_cc.c create mode 100644 src/PJ_cc.cpp delete mode 100644 src/PJ_ccon.c create mode 100644 src/PJ_ccon.cpp delete mode 100644 src/PJ_cea.c create mode 100644 src/PJ_cea.cpp delete mode 100644 src/PJ_chamb.c create mode 100644 src/PJ_chamb.cpp delete mode 100644 src/PJ_collg.c create mode 100644 src/PJ_collg.cpp delete mode 100644 src/PJ_comill.c create mode 100644 src/PJ_comill.cpp delete mode 100644 src/PJ_crast.c create mode 100644 src/PJ_crast.cpp delete mode 100644 src/PJ_deformation.c create mode 100644 src/PJ_deformation.cpp delete mode 100644 src/PJ_denoy.c create mode 100644 src/PJ_denoy.cpp delete mode 100644 src/PJ_eck1.c create mode 100644 src/PJ_eck1.cpp delete mode 100644 src/PJ_eck2.c create mode 100644 src/PJ_eck2.cpp delete mode 100644 src/PJ_eck3.c create mode 100644 src/PJ_eck3.cpp delete mode 100644 src/PJ_eck4.c create mode 100644 src/PJ_eck4.cpp delete mode 100644 src/PJ_eck5.c create mode 100644 src/PJ_eck5.cpp delete mode 100644 src/PJ_eqc.c create mode 100644 src/PJ_eqc.cpp delete mode 100644 src/PJ_eqdc.c create mode 100644 src/PJ_eqdc.cpp delete mode 100644 src/PJ_eqearth.c create mode 100644 src/PJ_eqearth.cpp delete mode 100644 src/PJ_fahey.c create mode 100644 src/PJ_fahey.cpp delete mode 100644 src/PJ_fouc_s.c create mode 100644 src/PJ_fouc_s.cpp delete mode 100644 src/PJ_gall.c create mode 100644 src/PJ_gall.cpp delete mode 100644 src/PJ_geoc.c create mode 100644 src/PJ_geoc.cpp delete mode 100644 src/PJ_geos.c create mode 100644 src/PJ_geos.cpp delete mode 100644 src/PJ_gins8.c create mode 100644 src/PJ_gins8.cpp delete mode 100644 src/PJ_gn_sinu.c create mode 100644 src/PJ_gn_sinu.cpp delete mode 100644 src/PJ_gnom.c create mode 100644 src/PJ_gnom.cpp delete mode 100644 src/PJ_goode.c create mode 100644 src/PJ_goode.cpp delete mode 100644 src/PJ_gstmerc.c create mode 100644 src/PJ_gstmerc.cpp delete mode 100644 src/PJ_hammer.c create mode 100644 src/PJ_hammer.cpp delete mode 100644 src/PJ_hatano.c create mode 100644 src/PJ_hatano.cpp delete mode 100644 src/PJ_healpix.c create mode 100644 src/PJ_healpix.cpp delete mode 100644 src/PJ_helmert.c create mode 100644 src/PJ_helmert.cpp delete mode 100644 src/PJ_hgridshift.c create mode 100644 src/PJ_hgridshift.cpp delete mode 100644 src/PJ_horner.c create mode 100644 src/PJ_horner.cpp delete mode 100644 src/PJ_igh.c create mode 100644 src/PJ_igh.cpp delete mode 100644 src/PJ_imw_p.c create mode 100644 src/PJ_imw_p.cpp delete mode 100644 src/PJ_isea.c create mode 100644 src/PJ_isea.cpp delete mode 100644 src/PJ_krovak.c create mode 100644 src/PJ_krovak.cpp delete mode 100644 src/PJ_labrd.c create mode 100644 src/PJ_labrd.cpp delete mode 100644 src/PJ_laea.c create mode 100644 src/PJ_laea.cpp delete mode 100644 src/PJ_lagrng.c create mode 100644 src/PJ_lagrng.cpp delete mode 100644 src/PJ_larr.c create mode 100644 src/PJ_larr.cpp delete mode 100644 src/PJ_lask.c create mode 100644 src/PJ_lask.cpp delete mode 100644 src/PJ_latlong.c create mode 100644 src/PJ_latlong.cpp delete mode 100644 src/PJ_lcc.c create mode 100644 src/PJ_lcc.cpp delete mode 100644 src/PJ_lcca.c create mode 100644 src/PJ_lcca.cpp delete mode 100644 src/PJ_loxim.c create mode 100644 src/PJ_loxim.cpp delete mode 100644 src/PJ_lsat.c create mode 100644 src/PJ_lsat.cpp delete mode 100644 src/PJ_mbt_fps.c create mode 100644 src/PJ_mbt_fps.cpp delete mode 100644 src/PJ_mbtfpp.c create mode 100644 src/PJ_mbtfpp.cpp delete mode 100644 src/PJ_mbtfpq.c create mode 100644 src/PJ_mbtfpq.cpp delete mode 100644 src/PJ_merc.c create mode 100644 src/PJ_merc.cpp delete mode 100644 src/PJ_mill.c create mode 100644 src/PJ_mill.cpp delete mode 100644 src/PJ_misrsom.c create mode 100644 src/PJ_misrsom.cpp delete mode 100644 src/PJ_mod_ster.c create mode 100644 src/PJ_mod_ster.cpp delete mode 100644 src/PJ_moll.c create mode 100644 src/PJ_moll.cpp delete mode 100644 src/PJ_molodensky.c create mode 100644 src/PJ_molodensky.cpp delete mode 100644 src/PJ_natearth.c create mode 100644 src/PJ_natearth.cpp delete mode 100644 src/PJ_natearth2.c create mode 100644 src/PJ_natearth2.cpp delete mode 100644 src/PJ_nell.c create mode 100644 src/PJ_nell.cpp delete mode 100644 src/PJ_nell_h.c create mode 100644 src/PJ_nell_h.cpp delete mode 100644 src/PJ_nocol.c create mode 100644 src/PJ_nocol.cpp delete mode 100644 src/PJ_nsper.c create mode 100644 src/PJ_nsper.cpp delete mode 100644 src/PJ_nzmg.c create mode 100644 src/PJ_nzmg.cpp delete mode 100644 src/PJ_ob_tran.c create mode 100644 src/PJ_ob_tran.cpp delete mode 100644 src/PJ_ocea.c create mode 100644 src/PJ_ocea.cpp delete mode 100644 src/PJ_oea.c create mode 100644 src/PJ_oea.cpp delete mode 100644 src/PJ_omerc.c create mode 100644 src/PJ_omerc.cpp delete mode 100644 src/PJ_ortho.c create mode 100644 src/PJ_ortho.cpp delete mode 100644 src/PJ_patterson.c create mode 100644 src/PJ_patterson.cpp delete mode 100644 src/PJ_pipeline.c create mode 100644 src/PJ_pipeline.cpp delete mode 100644 src/PJ_poly.c create mode 100644 src/PJ_poly.cpp delete mode 100644 src/PJ_putp2.c create mode 100644 src/PJ_putp2.cpp delete mode 100644 src/PJ_putp3.c create mode 100644 src/PJ_putp3.cpp delete mode 100644 src/PJ_putp4p.c create mode 100644 src/PJ_putp4p.cpp delete mode 100644 src/PJ_putp5.c create mode 100644 src/PJ_putp5.cpp delete mode 100644 src/PJ_putp6.c create mode 100644 src/PJ_putp6.cpp delete mode 100644 src/PJ_qsc.c create mode 100644 src/PJ_qsc.cpp delete mode 100644 src/PJ_robin.c create mode 100644 src/PJ_robin.cpp delete mode 100644 src/PJ_rpoly.c create mode 100644 src/PJ_rpoly.cpp delete mode 100644 src/PJ_sch.c create mode 100644 src/PJ_sch.cpp delete mode 100644 src/PJ_sconics.c create mode 100644 src/PJ_sconics.cpp delete mode 100644 src/PJ_somerc.c create mode 100644 src/PJ_somerc.cpp delete mode 100644 src/PJ_stere.c create mode 100644 src/PJ_stere.cpp delete mode 100644 src/PJ_sterea.c create mode 100644 src/PJ_sterea.cpp delete mode 100644 src/PJ_sts.c create mode 100644 src/PJ_sts.cpp delete mode 100644 src/PJ_tcc.c create mode 100644 src/PJ_tcc.cpp delete mode 100644 src/PJ_tcea.c create mode 100644 src/PJ_tcea.cpp delete mode 100644 src/PJ_times.c create mode 100644 src/PJ_times.cpp delete mode 100644 src/PJ_tmerc.c create mode 100644 src/PJ_tmerc.cpp delete mode 100644 src/PJ_tobmerc.c create mode 100644 src/PJ_tobmerc.cpp delete mode 100644 src/PJ_tpeqd.c create mode 100644 src/PJ_tpeqd.cpp delete mode 100644 src/PJ_unitconvert.c create mode 100644 src/PJ_unitconvert.cpp delete mode 100644 src/PJ_urm5.c create mode 100644 src/PJ_urm5.cpp delete mode 100644 src/PJ_urmfps.c create mode 100644 src/PJ_urmfps.cpp delete mode 100644 src/PJ_vandg.c create mode 100644 src/PJ_vandg.cpp delete mode 100644 src/PJ_vandg2.c create mode 100644 src/PJ_vandg2.cpp delete mode 100644 src/PJ_vandg4.c create mode 100644 src/PJ_vandg4.cpp delete mode 100644 src/PJ_vgridshift.c create mode 100644 src/PJ_vgridshift.cpp delete mode 100644 src/PJ_wag2.c create mode 100644 src/PJ_wag2.cpp delete mode 100644 src/PJ_wag3.c create mode 100644 src/PJ_wag3.cpp delete mode 100644 src/PJ_wag7.c create mode 100644 src/PJ_wag7.cpp delete mode 100644 src/PJ_wink1.c create mode 100644 src/PJ_wink1.cpp delete mode 100644 src/PJ_wink2.c create mode 100644 src/PJ_wink2.cpp delete mode 100644 src/aasincos.c create mode 100644 src/aasincos.cpp delete mode 100644 src/adjlon.c create mode 100644 src/adjlon.cpp delete mode 100644 src/bch2bps.c create mode 100644 src/bch2bps.cpp delete mode 100644 src/bchgen.c create mode 100644 src/bchgen.cpp delete mode 100644 src/biveval.c create mode 100644 src/biveval.cpp delete mode 100644 src/cct.c create mode 100644 src/cct.cpp delete mode 100644 src/dmstor.c create mode 100644 src/dmstor.cpp delete mode 100644 src/emess.c create mode 100644 src/emess.cpp delete mode 100644 src/gen_cheb.c create mode 100644 src/gen_cheb.cpp delete mode 100644 src/geocent.c create mode 100644 src/geocent.cpp delete mode 100644 src/geod.c create mode 100644 src/geod.cpp delete mode 100644 src/geod_interface.c create mode 100644 src/geod_interface.cpp delete mode 100644 src/geod_set.c create mode 100644 src/geod_set.cpp delete mode 100644 src/geodesic.c create mode 100644 src/geodesic.cpp delete mode 100644 src/geodtest.c create mode 100644 src/geodtest.cpp delete mode 100644 src/gie.c create mode 100644 src/gie.cpp delete mode 100644 src/jniproj.c create mode 100644 src/jniproj.cpp delete mode 100644 src/mk_cheby.c create mode 100644 src/mk_cheby.cpp delete mode 100644 src/multistresstest.c create mode 100644 src/multistresstest.cpp delete mode 100644 src/nad2bin.c create mode 100644 src/nad2bin.cpp delete mode 100644 src/nad_cvt.c create mode 100644 src/nad_cvt.cpp delete mode 100644 src/nad_init.c create mode 100644 src/nad_init.cpp delete mode 100644 src/nad_intr.c create mode 100644 src/nad_intr.cpp delete mode 100644 src/p_series.c create mode 100644 src/p_series.cpp delete mode 100644 src/pj_apply_gridshift.c create mode 100644 src/pj_apply_gridshift.cpp delete mode 100644 src/pj_apply_vgridshift.c create mode 100644 src/pj_apply_vgridshift.cpp delete mode 100644 src/pj_auth.c create mode 100644 src/pj_auth.cpp delete mode 100644 src/pj_ctx.c create mode 100644 src/pj_ctx.cpp delete mode 100644 src/pj_datum_set.c create mode 100644 src/pj_datum_set.cpp delete mode 100644 src/pj_datums.c create mode 100644 src/pj_datums.cpp delete mode 100644 src/pj_deriv.c create mode 100644 src/pj_deriv.cpp delete mode 100644 src/pj_ell_set.c create mode 100644 src/pj_ell_set.cpp delete mode 100644 src/pj_ellps.c create mode 100644 src/pj_ellps.cpp delete mode 100644 src/pj_errno.c create mode 100644 src/pj_errno.cpp delete mode 100644 src/pj_factors.c create mode 100644 src/pj_factors.cpp delete mode 100644 src/pj_fileapi.c create mode 100644 src/pj_fileapi.cpp delete mode 100644 src/pj_fwd.c create mode 100644 src/pj_fwd.cpp delete mode 100644 src/pj_gauss.c create mode 100644 src/pj_gauss.cpp delete mode 100644 src/pj_gc_reader.c create mode 100644 src/pj_gc_reader.cpp delete mode 100644 src/pj_geocent.c create mode 100644 src/pj_geocent.cpp delete mode 100644 src/pj_gridcatalog.c create mode 100644 src/pj_gridcatalog.cpp delete mode 100644 src/pj_gridinfo.c create mode 100644 src/pj_gridinfo.cpp delete mode 100644 src/pj_gridlist.c create mode 100644 src/pj_gridlist.cpp delete mode 100644 src/pj_init.c create mode 100644 src/pj_init.cpp delete mode 100644 src/pj_initcache.c create mode 100644 src/pj_initcache.cpp delete mode 100644 src/pj_internal.c create mode 100644 src/pj_internal.cpp delete mode 100644 src/pj_inv.c create mode 100644 src/pj_inv.cpp delete mode 100644 src/pj_list.c create mode 100644 src/pj_list.cpp delete mode 100644 src/pj_log.c create mode 100644 src/pj_log.cpp delete mode 100644 src/pj_malloc.c create mode 100644 src/pj_malloc.cpp delete mode 100644 src/pj_math.c create mode 100644 src/pj_math.cpp delete mode 100644 src/pj_mlfn.c create mode 100644 src/pj_mlfn.cpp delete mode 100644 src/pj_msfn.c create mode 100644 src/pj_msfn.cpp delete mode 100644 src/pj_mutex.c create mode 100644 src/pj_mutex.cpp delete mode 100644 src/pj_open_lib.c create mode 100644 src/pj_open_lib.cpp delete mode 100644 src/pj_param.c create mode 100644 src/pj_param.cpp delete mode 100644 src/pj_phi2.c create mode 100644 src/pj_phi2.cpp delete mode 100644 src/pj_pr_list.c create mode 100644 src/pj_pr_list.cpp delete mode 100644 src/pj_qsfn.c create mode 100644 src/pj_qsfn.cpp delete mode 100644 src/pj_release.c create mode 100644 src/pj_release.cpp delete mode 100644 src/pj_strerrno.c create mode 100644 src/pj_strerrno.cpp delete mode 100644 src/pj_strtod.c create mode 100644 src/pj_strtod.cpp delete mode 100644 src/pj_transform.c create mode 100644 src/pj_transform.cpp delete mode 100644 src/pj_tsfn.c create mode 100644 src/pj_tsfn.cpp delete mode 100644 src/pj_units.c create mode 100644 src/pj_units.cpp delete mode 100644 src/pj_utils.c create mode 100644 src/pj_utils.cpp delete mode 100644 src/pj_zpoly1.c create mode 100644 src/pj_zpoly1.cpp delete mode 100644 src/proj.c create mode 100644 src/proj.cpp delete mode 100644 src/proj_4D_api.c create mode 100644 src/proj_4D_api.cpp delete mode 100644 src/proj_etmerc.c create mode 100644 src/proj_etmerc.cpp delete mode 100644 src/proj_mdist.c create mode 100644 src/proj_mdist.cpp delete mode 100644 src/proj_rouss.c create mode 100644 src/proj_rouss.cpp delete mode 100644 src/proj_strtod.c create mode 100644 src/proj_strtod.cpp delete mode 100644 src/rtodms.c create mode 100644 src/rtodms.cpp delete mode 100644 src/test228.c create mode 100644 src/test228.cpp delete mode 100644 src/vector1.c create mode 100644 src/vector1.cpp (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index f484acd8..990ca74d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,20 +15,20 @@ include_HEADERS = proj.h proj_experimental.h proj_constants.h proj_api.h geodesi EXTRA_DIST = bin_cct.cmake bin_gie.cmake bin_cs2cs.cmake \ bin_geod.cmake bin_nad2bin.cmake bin_proj.cmake bin_projinfo.cmake \ - lib_proj.cmake CMakeLists.txt bin_geodtest.cmake geodtest.c \ + lib_proj.cmake CMakeLists.txt bin_geodtest.cmake geodtest.cpp \ pj_wkt1_grammar.y pj_wkt2_grammar.y -proj_SOURCES = proj.c gen_cheb.c p_series.c +proj_SOURCES = proj.cpp gen_cheb.cpp p_series.cpp projinfo_SOURCES = projinfo.cpp -cs2cs_SOURCES = cs2cs.cpp gen_cheb.c p_series.c -cct_SOURCES = cct.c proj_strtod.c proj_strtod.h optargpm.h -nad2bin_SOURCES = nad2bin.c -geod_SOURCES = geod.c geod_set.c geod_interface.c geod_interface.h +cs2cs_SOURCES = cs2cs.cpp gen_cheb.cpp p_series.cpp +cct_SOURCES = cct.cpp proj_strtod.cpp proj_strtod.h optargpm.h +nad2bin_SOURCES = nad2bin.cpp +geod_SOURCES = geod.cpp geod_set.cpp geod_interface.cpp geod_interface.h -gie_SOURCES = gie.c proj_strtod.c proj_strtod.h optargpm.h -multistresstest_SOURCES = multistresstest.c -test228_SOURCES = test228.c -geodtest_SOURCES = geodtest.c +gie_SOURCES = gie.cpp proj_strtod.cpp proj_strtod.h optargpm.h +multistresstest_SOURCES = multistresstest.cpp +test228_SOURCES = test228.cpp +geodtest_SOURCES = geodtest.cpp cct_LDADD = libproj.la cs2cs_LDADD = libproj.la @@ -51,51 +51,51 @@ libproj_la_SOURCES = \ pj_list.h proj_internal.h proj_math.h projects.h\ static.cpp util.cpp metadata.cpp common.cpp crs.cpp datum.cpp coordinatesystem.cpp coordinateoperation.cpp io.cpp \ internal.cpp factory.cpp c_api.cpp \ - PJ_aeqd.c PJ_gnom.c PJ_laea.c PJ_mod_ster.c \ - PJ_nsper.c PJ_nzmg.c PJ_ortho.c PJ_stere.c PJ_sterea.c \ - PJ_aea.c PJ_bipc.c PJ_bonne.c PJ_eqdc.c PJ_isea.c PJ_ccon.c\ - PJ_imw_p.c PJ_krovak.c PJ_lcc.c PJ_poly.c \ - PJ_rpoly.c PJ_sconics.c proj_rouss.c \ - PJ_cass.c PJ_cc.c PJ_cea.c PJ_eqc.c PJ_gall.c PJ_geoc.c \ - PJ_labrd.c PJ_lsat.c PJ_misrsom.c PJ_merc.c \ - PJ_mill.c PJ_ocea.c PJ_omerc.c PJ_somerc.c \ - PJ_tcc.c PJ_tcea.c PJ_times.c PJ_tmerc.c PJ_tobmerc.c \ - PJ_airy.c PJ_aitoff.c PJ_august.c PJ_bacon.c \ - PJ_bertin1953.c PJ_chamb.c PJ_hammer.c PJ_lagrng.c PJ_larr.c \ - PJ_lask.c PJ_latlong.c PJ_nocol.c PJ_ob_tran.c PJ_oea.c \ - PJ_tpeqd.c PJ_vandg.c PJ_vandg2.c PJ_vandg4.c \ - PJ_wag7.c PJ_lcca.c PJ_geos.c proj_etmerc.c \ - PJ_boggs.c PJ_collg.c PJ_comill.c PJ_crast.c PJ_denoy.c \ - PJ_eck1.c PJ_eck2.c PJ_eck3.c PJ_eck4.c \ - PJ_eck5.c PJ_fahey.c PJ_fouc_s.c PJ_gins8.c PJ_gstmerc.c \ - PJ_gn_sinu.c PJ_goode.c PJ_igh.c PJ_hatano.c PJ_loxim.c \ - PJ_mbt_fps.c PJ_mbtfpp.c PJ_mbtfpq.c PJ_moll.c \ - PJ_nell.c PJ_nell_h.c PJ_patterson.c PJ_putp2.c PJ_putp3.c \ - PJ_putp4p.c PJ_putp5.c PJ_putp6.c PJ_qsc.c PJ_robin.c \ - PJ_sch.c PJ_sts.c PJ_urm5.c PJ_urmfps.c PJ_wag2.c \ - PJ_wag3.c PJ_wink1.c PJ_wink2.c pj_geocent.c \ - aasincos.c adjlon.c bch2bps.c bchgen.c \ - biveval.c dmstor.c mk_cheby.c pj_auth.c \ - pj_deriv.c pj_ell_set.c pj_ellps.c pj_errno.c \ - pj_factors.c pj_fwd.c pj_init.c pj_inv.c \ - pj_list.c pj_malloc.c pj_mlfn.c pj_msfn.c proj_mdist.c \ - pj_open_lib.c pj_param.c pj_phi2.c pj_pr_list.c \ - pj_qsfn.c pj_strerrno.c \ - pj_tsfn.c pj_units.c pj_ctx.c pj_log.c pj_zpoly1.c rtodms.c \ - vector1.c pj_release.c pj_gauss.c \ - PJ_healpix.c PJ_natearth.c PJ_natearth2.c PJ_calcofi.c pj_fileapi.c \ - PJ_eqearth.c \ + PJ_aeqd.cpp PJ_gnom.cpp PJ_laea.cpp PJ_mod_ster.cpp \ + PJ_nsper.cpp PJ_nzmg.cpp PJ_ortho.cpp PJ_stere.cpp PJ_sterea.cpp \ + PJ_aea.cpp PJ_bipc.cpp PJ_bonne.cpp PJ_eqdc.cpp PJ_isea.cpp PJ_ccon.cpp \ + PJ_imw_p.cpp PJ_krovak.cpp PJ_lcc.cpp PJ_poly.cpp \ + PJ_rpoly.cpp PJ_sconics.cpp proj_rouss.cpp \ + PJ_cass.cpp PJ_cc.cpp PJ_cea.cpp PJ_eqc.cpp PJ_gall.cpp PJ_geoc.cpp \ + PJ_labrd.cpp PJ_lsat.cpp PJ_misrsom.cpp PJ_merc.cpp \ + PJ_mill.cpp PJ_ocea.cpp PJ_omerc.cpp PJ_somerc.cpp \ + PJ_tcc.cpp PJ_tcea.cpp PJ_times.cpp PJ_tmerc.cpp PJ_tobmerc.cpp \ + PJ_airy.cpp PJ_aitoff.cpp PJ_august.cpp PJ_bacon.cpp \ + PJ_bertin1953.cpp PJ_chamb.cpp PJ_hammer.cpp PJ_lagrng.cpp PJ_larr.cpp \ + PJ_lask.cpp PJ_latlong.cpp PJ_nocol.cpp PJ_ob_tran.cpp PJ_oea.cpp \ + PJ_tpeqd.cpp PJ_vandg.cpp PJ_vandg2.cpp PJ_vandg4.cpp \ + PJ_wag7.cpp PJ_lcca.cpp PJ_geos.cpp proj_etmerc.cpp \ + PJ_boggs.cpp PJ_collg.cpp PJ_comill.cpp PJ_crast.cpp PJ_denoy.cpp \ + PJ_eck1.cpp PJ_eck2.cpp PJ_eck3.cpp PJ_eck4.cpp \ + PJ_eck5.cpp PJ_fahey.cpp PJ_fouc_s.cpp PJ_gins8.cpp PJ_gstmerc.cpp \ + PJ_gn_sinu.cpp PJ_goode.cpp PJ_igh.cpp PJ_hatano.cpp PJ_loxim.cpp \ + PJ_mbt_fps.cpp PJ_mbtfpp.cpp PJ_mbtfpq.cpp PJ_moll.cpp \ + PJ_nell.cpp PJ_nell_h.cpp PJ_patterson.cpp PJ_putp2.cpp PJ_putp3.cpp \ + PJ_putp4p.cpp PJ_putp5.cpp PJ_putp6.cpp PJ_qsc.cpp PJ_robin.cpp \ + PJ_sch.cpp PJ_sts.cpp PJ_urm5.cpp PJ_urmfps.cpp PJ_wag2.cpp \ + PJ_wag3.cpp PJ_wink1.cpp PJ_wink2.cpp pj_geocent.cpp \ + aasincos.cpp adjlon.cpp bch2bps.cpp bchgen.cpp \ + biveval.cpp dmstor.cpp mk_cheby.cpp pj_auth.cpp \ + pj_deriv.cpp pj_ell_set.cpp pj_ellps.cpp pj_errno.cpp \ + pj_factors.cpp pj_fwd.cpp pj_init.cpp pj_inv.cpp \ + pj_list.cpp pj_malloc.cpp pj_mlfn.cpp pj_msfn.cpp proj_mdist.cpp \ + pj_open_lib.cpp pj_param.cpp pj_phi2.cpp pj_pr_list.cpp \ + pj_qsfn.cpp pj_strerrno.cpp \ + pj_tsfn.cpp pj_units.cpp pj_ctx.cpp pj_log.cpp pj_zpoly1.cpp rtodms.cpp \ + vector1.cpp pj_release.cpp pj_gauss.cpp \ + PJ_healpix.cpp PJ_natearth.cpp PJ_natearth2.cpp PJ_calcofi.cpp pj_fileapi.cpp \ + PJ_eqearth.cpp \ \ - pj_gc_reader.c pj_gridcatalog.c \ - nad_cvt.c nad_init.c nad_intr.c emess.c emess.h \ - pj_apply_gridshift.c pj_datums.c pj_datum_set.c pj_transform.c \ - geocent.c geocent.h pj_utils.c pj_gridinfo.c pj_gridlist.c \ - jniproj.c pj_mutex.c pj_initcache.c pj_apply_vgridshift.c geodesic.c \ - pj_strtod.c pj_math.c\ + pj_gc_reader.cpp pj_gridcatalog.cpp \ + nad_cvt.cpp nad_init.cpp nad_intr.cpp emess.cpp emess.h \ + pj_apply_gridshift.cpp pj_datums.cpp pj_datum_set.cpp pj_transform.cpp \ + geocent.cpp geocent.h pj_utils.cpp pj_gridinfo.cpp pj_gridlist.cpp \ + jniproj.cpp pj_mutex.cpp pj_initcache.cpp pj_apply_vgridshift.cpp geodesic.cpp \ + pj_strtod.cpp pj_math.cpp \ \ - proj_4D_api.c PJ_cart.c PJ_pipeline.c PJ_horner.c PJ_helmert.c \ - PJ_vgridshift.c PJ_hgridshift.c PJ_unitconvert.c PJ_molodensky.c \ - PJ_deformation.c pj_internal.c PJ_axisswap.c PJ_affine.c \ + proj_4D_api.cpp PJ_cart.cpp PJ_pipeline.cpp PJ_horner.cpp PJ_helmert.cpp \ + PJ_vgridshift.cpp PJ_hgridshift.cpp PJ_unitconvert.cpp PJ_molodensky.cpp \ + PJ_deformation.cpp pj_internal.cpp PJ_axisswap.cpp PJ_affine.cpp \ pj_wkt_parser.hpp pj_wkt_parser.cpp \ pj_wkt1_parser.h pj_wkt1_parser.cpp \ pj_wkt1_generated_parser.h pj_wkt1_generated_parser.c \ diff --git a/src/PJ_aea.c b/src/PJ_aea.c deleted file mode 100644 index 640f1013..00000000 --- a/src/PJ_aea.c +++ /dev/null @@ -1,222 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the aea (Albers Equal Area) projection. - * and the leac (Lambert Equal Area Conic) projection - * Author: Gerald Evenden (1995) - * Thomas Knudsen (2016) - revise/add regression tests - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ -#include "proj.h" -#include -#include "projects.h" -#include "proj_math.h" - - -# define EPS10 1.e-10 -# define TOL7 1.e-7 - -PROJ_HEAD(aea, "Albers Equal Area") "\n\tConic Sph&Ell\n\tlat_1= lat_2="; -PROJ_HEAD(leac, "Lambert Equal Area Conic") "\n\tConic, Sph&Ell\n\tlat_1= south"; - - -/* determine latitude angle phi-1 */ -# define N_ITER 15 -# define EPSILON 1.0e-7 -# define TOL 1.0e-10 -static double phi1_(double qs, double Te, double Tone_es) { - int i; - double Phi, sinpi, cospi, con, com, dphi; - - Phi = asin (.5 * qs); - if (Te < EPSILON) - return( Phi ); - i = N_ITER; - do { - sinpi = sin (Phi); - cospi = cos (Phi); - con = Te * sinpi; - com = 1. - con * con; - dphi = .5 * com * com / cospi * (qs / Tone_es - - sinpi / com + .5 / Te * log ((1. - con) / - (1. + con))); - Phi += dphi; - } while (fabs(dphi) > TOL && --i); - return( i ? Phi : HUGE_VAL ); -} - - -struct pj_opaque { - double ec; - double n; - double c; - double dd; - double n2; - double rho0; - double rho; - double phi1; - double phi2; - double *en; - int ellips; -}; - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - - - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoid/spheroid, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - Q->rho = Q->c - (Q->ellips ? Q->n * pj_qsfn(sin(lp.phi), P->e, P->one_es) : Q->n2 * sin(lp.phi));; - if (Q->rho < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - Q->rho = Q->dd * sqrt(Q->rho); - xy.x = Q->rho * sin( lp.lam *= Q->n ); - xy.y = Q->rho0 - Q->rho * cos(lp.lam); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoid/spheroid, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - if( (Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { - if (Q->n < 0.) { - Q->rho = -Q->rho; - xy.x = -xy.x; - xy.y = -xy.y; - } - lp.phi = Q->rho / Q->dd; - if (Q->ellips) { - lp.phi = (Q->c - lp.phi * lp.phi) / Q->n; - if (fabs(Q->ec - fabs(lp.phi)) > TOL7) { - if ((lp.phi = phi1_(lp.phi, P->e, P->one_es)) == HUGE_VAL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - } else - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - } else if (fabs(lp.phi = (Q->c - lp.phi * lp.phi) / Q->n2) <= 1.) - lp.phi = asin(lp.phi); - else - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - lp.lam = atan2(xy.x, xy.y) / Q->n; - } else { - lp.lam = 0.; - lp.phi = Q->n > 0. ? M_HALFPI : - M_HALFPI; - } - return lp; -} - - - -static PJ *setup(PJ *P) { - double cosphi, sinphi; - int secant; - struct pj_opaque *Q = P->opaque; - - P->inv = e_inverse; - P->fwd = e_forward; - - if (fabs(Q->phi1 + Q->phi2) < EPS10) - return destructor(P, PJD_ERR_CONIC_LAT_EQUAL); - Q->n = sinphi = sin(Q->phi1); - cosphi = cos(Q->phi1); - secant = fabs(Q->phi1 - Q->phi2) >= EPS10; - if( (Q->ellips = (P->es > 0.))) { - double ml1, m1; - - if (!(Q->en = pj_enfn(P->es))) - return destructor(P, 0); - m1 = pj_msfn(sinphi, cosphi, P->es); - ml1 = pj_qsfn(sinphi, P->e, P->one_es); - if (secant) { /* secant cone */ - double ml2, m2; - - sinphi = sin(Q->phi2); - cosphi = cos(Q->phi2); - m2 = pj_msfn(sinphi, cosphi, P->es); - ml2 = pj_qsfn(sinphi, P->e, P->one_es); - if (ml2 == ml1) - return destructor(P, 0); - - Q->n = (m1 * m1 - m2 * m2) / (ml2 - ml1); - } - Q->ec = 1. - .5 * P->one_es * log((1. - P->e) / - (1. + P->e)) / P->e; - Q->c = m1 * m1 + Q->n * ml1; - Q->dd = 1. / Q->n; - Q->rho0 = Q->dd * sqrt(Q->c - Q->n * pj_qsfn(sin(P->phi0), - P->e, P->one_es)); - } else { - if (secant) Q->n = .5 * (Q->n + sin(Q->phi2)); - Q->n2 = Q->n + Q->n; - Q->c = cosphi * cosphi + Q->n2 * sinphi; - Q->dd = 1. / Q->n; - Q->rho0 = Q->dd * sqrt(Q->c - Q->n2 * sin(P->phi0)); - } - - return P; -} - - -PJ *PROJECTION(aea) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - return setup(P); -} - - -PJ *PROJECTION(leac) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->phi2 = pj_param(P->ctx, P->params, "rlat_1").f; - Q->phi1 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI; - return setup(P); -} - diff --git a/src/PJ_aea.cpp b/src/PJ_aea.cpp new file mode 100644 index 00000000..e4d1dc4f --- /dev/null +++ b/src/PJ_aea.cpp @@ -0,0 +1,222 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the aea (Albers Equal Area) projection. + * and the leac (Lambert Equal Area Conic) projection + * Author: Gerald Evenden (1995) + * Thomas Knudsen (2016) - revise/add regression tests + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ +#include "proj.h" +#include +#include "projects.h" +#include "proj_math.h" + + +# define EPS10 1.e-10 +# define TOL7 1.e-7 + +PROJ_HEAD(aea, "Albers Equal Area") "\n\tConic Sph&Ell\n\tlat_1= lat_2="; +PROJ_HEAD(leac, "Lambert Equal Area Conic") "\n\tConic, Sph&Ell\n\tlat_1= south"; + + +/* determine latitude angle phi-1 */ +# define N_ITER 15 +# define EPSILON 1.0e-7 +# define TOL 1.0e-10 +static double phi1_(double qs, double Te, double Tone_es) { + int i; + double Phi, sinpi, cospi, con, com, dphi; + + Phi = asin (.5 * qs); + if (Te < EPSILON) + return( Phi ); + i = N_ITER; + do { + sinpi = sin (Phi); + cospi = cos (Phi); + con = Te * sinpi; + com = 1. - con * con; + dphi = .5 * com * com / cospi * (qs / Tone_es - + sinpi / com + .5 / Te * log ((1. - con) / + (1. + con))); + Phi += dphi; + } while (fabs(dphi) > TOL && --i); + return( i ? Phi : HUGE_VAL ); +} + + +struct pj_opaque { + double ec; + double n; + double c; + double dd; + double n2; + double rho0; + double rho; + double phi1; + double phi2; + double *en; + int ellips; +}; + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoid/spheroid, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + Q->rho = Q->c - (Q->ellips ? Q->n * pj_qsfn(sin(lp.phi), P->e, P->one_es) : Q->n2 * sin(lp.phi));; + if (Q->rho < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + Q->rho = Q->dd * sqrt(Q->rho); + xy.x = Q->rho * sin( lp.lam *= Q->n ); + xy.y = Q->rho0 - Q->rho * cos(lp.lam); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoid/spheroid, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + if( (Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { + if (Q->n < 0.) { + Q->rho = -Q->rho; + xy.x = -xy.x; + xy.y = -xy.y; + } + lp.phi = Q->rho / Q->dd; + if (Q->ellips) { + lp.phi = (Q->c - lp.phi * lp.phi) / Q->n; + if (fabs(Q->ec - fabs(lp.phi)) > TOL7) { + if ((lp.phi = phi1_(lp.phi, P->e, P->one_es)) == HUGE_VAL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + } else + lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + } else if (fabs(lp.phi = (Q->c - lp.phi * lp.phi) / Q->n2) <= 1.) + lp.phi = asin(lp.phi); + else + lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + lp.lam = atan2(xy.x, xy.y) / Q->n; + } else { + lp.lam = 0.; + lp.phi = Q->n > 0. ? M_HALFPI : - M_HALFPI; + } + return lp; +} + + + +static PJ *setup(PJ *P) { + double cosphi, sinphi; + int secant; + struct pj_opaque *Q = static_cast(P->opaque); + + P->inv = e_inverse; + P->fwd = e_forward; + + if (fabs(Q->phi1 + Q->phi2) < EPS10) + return destructor(P, PJD_ERR_CONIC_LAT_EQUAL); + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if( (Q->ellips = (P->es > 0.))) { + double ml1, m1; + + if (!(Q->en = pj_enfn(P->es))) + return destructor(P, 0); + m1 = pj_msfn(sinphi, cosphi, P->es); + ml1 = pj_qsfn(sinphi, P->e, P->one_es); + if (secant) { /* secant cone */ + double ml2, m2; + + sinphi = sin(Q->phi2); + cosphi = cos(Q->phi2); + m2 = pj_msfn(sinphi, cosphi, P->es); + ml2 = pj_qsfn(sinphi, P->e, P->one_es); + if (ml2 == ml1) + return destructor(P, 0); + + Q->n = (m1 * m1 - m2 * m2) / (ml2 - ml1); + } + Q->ec = 1. - .5 * P->one_es * log((1. - P->e) / + (1. + P->e)) / P->e; + Q->c = m1 * m1 + Q->n * ml1; + Q->dd = 1. / Q->n; + Q->rho0 = Q->dd * sqrt(Q->c - Q->n * pj_qsfn(sin(P->phi0), + P->e, P->one_es)); + } else { + if (secant) Q->n = .5 * (Q->n + sin(Q->phi2)); + Q->n2 = Q->n + Q->n; + Q->c = cosphi * cosphi + Q->n2 * sinphi; + Q->dd = 1. / Q->n; + Q->rho0 = Q->dd * sqrt(Q->c - Q->n2 * sin(P->phi0)); + } + + return P; +} + + +PJ *PROJECTION(aea) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + return setup(P); +} + + +PJ *PROJECTION(leac) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->phi2 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi1 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI; + return setup(P); +} + diff --git a/src/PJ_aeqd.c b/src/PJ_aeqd.c deleted file mode 100644 index 5d8c3d38..00000000 --- a/src/PJ_aeqd.c +++ /dev/null @@ -1,323 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the aeqd (Azimuthal Equidistant) projection. - * Author: Gerald Evenden - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ -#include "geodesic.h" -#include "proj.h" -#include -#include "projects.h" -#include "proj_math.h" - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double sinph0; - double cosph0; - double *en; - double M1; - double N1; - double Mp; - double He; - double G; - enum Mode mode; - struct geod_geodesic g; -}; - -PROJ_HEAD(aeqd, "Azimuthal Equidistant") "\n\tAzi, Sph&Ell\n\tlat_0 guam"; - -#define EPS10 1.e-10 -#define TOL 1.e-14 - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - - -static XY e_guam_fwd(LP lp, PJ *P) { /* Guam elliptical */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosphi, sinphi, t; - - cosphi = cos(lp.phi); - sinphi = sin(lp.phi); - t = 1. / sqrt(1. - P->es * sinphi * sinphi); - xy.x = lp.lam * cosphi * t; - xy.y = pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->M1 + - .5 * lp.lam * lp.lam * cosphi * sinphi * t; - - return xy; -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi, rho; - double azi1, azi2, s12; - double lam1, phi1, lam2, phi2; - - coslam = cos(lp.lam); - cosphi = cos(lp.phi); - sinphi = sin(lp.phi); - switch (Q->mode) { - case N_POLE: - coslam = - coslam; - /*-fallthrough*/ - case S_POLE: - xy.x = (rho = fabs(Q->Mp - pj_mlfn(lp.phi, sinphi, cosphi, Q->en))) * - sin(lp.lam); - xy.y = rho * coslam; - break; - case EQUIT: - case OBLIQ: - if (fabs(lp.lam) < EPS10 && fabs(lp.phi - P->phi0) < EPS10) { - xy.x = xy.y = 0.; - break; - } - - phi1 = P->phi0 / DEG_TO_RAD; lam1 = P->lam0 / DEG_TO_RAD; - phi2 = lp.phi / DEG_TO_RAD; lam2 = (lp.lam+P->lam0) / DEG_TO_RAD; - - geod_inverse(&Q->g, phi1, lam1, phi2, lam2, &s12, &azi1, &azi2); - azi1 *= DEG_TO_RAD; - xy.x = s12 * sin(azi1) / P->a; - xy.y = s12 * cos(azi1) / P->a; - break; - } - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (Q->mode) { - case EQUIT: - xy.y = cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; -oblcon: - if (fabs(fabs(xy.y) - 1.) < TOL) - if (xy.y < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - else - xy.x = xy.y = 0.; - else { - xy.y = acos(xy.y); - xy.y /= sin(xy.y); - xy.x = xy.y * cosphi * sin(lp.lam); - xy.y *= (Q->mode == EQUIT) ? sinphi : - Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; - } - break; - case N_POLE: - lp.phi = -lp.phi; - coslam = -coslam; - /*-fallthrough*/ - case S_POLE: - if (fabs(lp.phi - M_HALFPI) < EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = (xy.y = (M_HALFPI + lp.phi)) * sin(lp.lam); - xy.y *= coslam; - break; - } - return xy; -} - - -static LP e_guam_inv(XY xy, PJ *P) { /* Guam elliptical */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double x2, t = 0.0; - int i; - - x2 = 0.5 * xy.x * xy.x; - lp.phi = P->phi0; - for (i = 0; i < 3; ++i) { - t = P->e * sin(lp.phi); - lp.phi = pj_inv_mlfn(P->ctx, Q->M1 + xy.y - - x2 * tan(lp.phi) * (t = sqrt(1. - t * t)), P->es, Q->en); - } - lp.lam = xy.x * t / cos(lp.phi); - return lp; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c; - double azi1, azi2, s12, x2, y2, lat1, lon1, lat2, lon2; - - if ((c = hypot(xy.x, xy.y)) < EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - return (lp); - } - if (Q->mode == OBLIQ || Q->mode == EQUIT) { - - x2 = xy.x * P->a; - y2 = xy.y * P->a; - lat1 = P->phi0 / DEG_TO_RAD; - lon1 = P->lam0 / DEG_TO_RAD; - azi1 = atan2(x2, y2) / DEG_TO_RAD; - s12 = sqrt(x2 * x2 + y2 * y2); - geod_direct(&Q->g, lat1, lon1, azi1, s12, &lat2, &lon2, &azi2); - lp.phi = lat2 * DEG_TO_RAD; - lp.lam = lon2 * DEG_TO_RAD; - lp.lam -= P->lam0; - } else { /* Polar */ - lp.phi = pj_inv_mlfn(P->ctx, Q->mode == N_POLE ? Q->Mp - c : Q->Mp + c, - P->es, Q->en); - lp.lam = atan2(xy.x, Q->mode == N_POLE ? -xy.y : xy.y); - } - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosc, c_rh, sinc; - - if ((c_rh = hypot(xy.x, xy.y)) > M_PI) { - if (c_rh - EPS10 > M_PI) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - c_rh = M_PI; - } else if (c_rh < EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - return (lp); - } - if (Q->mode == OBLIQ || Q->mode == EQUIT) { - sinc = sin(c_rh); - cosc = cos(c_rh); - if (Q->mode == EQUIT) { - lp.phi = aasin(P->ctx, xy.y * sinc / c_rh); - xy.x *= sinc; - xy.y = cosc * c_rh; - } else { - lp.phi = aasin(P->ctx,cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 / - c_rh); - xy.y = (cosc - Q->sinph0 * sin(lp.phi)) * c_rh; - xy.x *= sinc * Q->cosph0; - } - lp.lam = xy.y == 0. ? 0. : atan2(xy.x, xy.y); - } else if (Q->mode == N_POLE) { - lp.phi = M_HALFPI - c_rh; - lp.lam = atan2(xy.x, -xy.y); - } else { - lp.phi = c_rh - M_HALFPI; - lp.lam = atan2(xy.x, xy.y); - } - return lp; -} - - -PJ *PROJECTION(aeqd) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - geod_init(&Q->g, P->a, P->es / (1 + sqrt(P->one_es))); - - if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) { - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - Q->sinph0 = P->phi0 < 0. ? -1. : 1.; - Q->cosph0 = 0.; - } else if (fabs(P->phi0) < EPS10) { - Q->mode = EQUIT; - Q->sinph0 = 0.; - Q->cosph0 = 1.; - } else { - Q->mode = OBLIQ; - Q->sinph0 = sin(P->phi0); - Q->cosph0 = cos(P->phi0); - } - if (P->es == 0.0) { - P->inv = s_inverse; - P->fwd = s_forward; - } else { - if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor (P, 0); - if (pj_param(P->ctx, P->params, "bguam").i) { - Q->M1 = pj_mlfn(P->phi0, Q->sinph0, Q->cosph0, Q->en); - P->inv = e_guam_inv; - P->fwd = e_guam_fwd; - } else { - switch (Q->mode) { - case N_POLE: - Q->Mp = pj_mlfn(M_HALFPI, 1., 0., Q->en); - break; - case S_POLE: - Q->Mp = pj_mlfn(-M_HALFPI, -1., 0., Q->en); - break; - case EQUIT: - case OBLIQ: - P->inv = e_inverse; P->fwd = e_forward; - Q->N1 = 1. / sqrt(1. - P->es * Q->sinph0 * Q->sinph0); - Q->G = Q->sinph0 * (Q->He = P->e / sqrt(P->one_es)); - Q->He *= Q->cosph0; - break; - } - P->inv = e_inverse; - P->fwd = e_forward; - } - } - - return P; -} - - diff --git a/src/PJ_aeqd.cpp b/src/PJ_aeqd.cpp new file mode 100644 index 00000000..72c7ae95 --- /dev/null +++ b/src/PJ_aeqd.cpp @@ -0,0 +1,323 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the aeqd (Azimuthal Equidistant) projection. + * Author: Gerald Evenden + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ +#include "geodesic.h" +#include "proj.h" +#include +#include "projects.h" +#include "proj_math.h" + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double sinph0; + double cosph0; + double *en; + double M1; + double N1; + double Mp; + double He; + double G; + enum Mode mode; + struct geod_geodesic g; +}; + +PROJ_HEAD(aeqd, "Azimuthal Equidistant") "\n\tAzi, Sph&Ell\n\tlat_0 guam"; + +#define EPS10 1.e-10 +#define TOL 1.e-14 + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + + +static XY e_guam_fwd(LP lp, PJ *P) { /* Guam elliptical */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosphi, sinphi, t; + + cosphi = cos(lp.phi); + sinphi = sin(lp.phi); + t = 1. / sqrt(1. - P->es * sinphi * sinphi); + xy.x = lp.lam * cosphi * t; + xy.y = pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->M1 + + .5 * lp.lam * lp.lam * cosphi * sinphi * t; + + return xy; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi, rho; + double azi1, azi2, s12; + double lam1, phi1, lam2, phi2; + + coslam = cos(lp.lam); + cosphi = cos(lp.phi); + sinphi = sin(lp.phi); + switch (Q->mode) { + case N_POLE: + coslam = - coslam; + /*-fallthrough*/ + case S_POLE: + xy.x = (rho = fabs(Q->Mp - pj_mlfn(lp.phi, sinphi, cosphi, Q->en))) * + sin(lp.lam); + xy.y = rho * coslam; + break; + case EQUIT: + case OBLIQ: + if (fabs(lp.lam) < EPS10 && fabs(lp.phi - P->phi0) < EPS10) { + xy.x = xy.y = 0.; + break; + } + + phi1 = P->phi0 / DEG_TO_RAD; lam1 = P->lam0 / DEG_TO_RAD; + phi2 = lp.phi / DEG_TO_RAD; lam2 = (lp.lam+P->lam0) / DEG_TO_RAD; + + geod_inverse(&Q->g, phi1, lam1, phi2, lam2, &s12, &azi1, &azi2); + azi1 *= DEG_TO_RAD; + xy.x = s12 * sin(azi1) / P->a; + xy.y = s12 * cos(azi1) / P->a; + break; + } + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y = cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; +oblcon: + if (fabs(fabs(xy.y) - 1.) < TOL) + if (xy.y < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + else + xy.x = xy.y = 0.; + else { + xy.y = acos(xy.y); + xy.y /= sin(xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + xy.y *= (Q->mode == EQUIT) ? sinphi : + Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; + } + break; + case N_POLE: + lp.phi = -lp.phi; + coslam = -coslam; + /*-fallthrough*/ + case S_POLE: + if (fabs(lp.phi - M_HALFPI) < EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = (xy.y = (M_HALFPI + lp.phi)) * sin(lp.lam); + xy.y *= coslam; + break; + } + return xy; +} + + +static LP e_guam_inv(XY xy, PJ *P) { /* Guam elliptical */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double x2, t = 0.0; + int i; + + x2 = 0.5 * xy.x * xy.x; + lp.phi = P->phi0; + for (i = 0; i < 3; ++i) { + t = P->e * sin(lp.phi); + lp.phi = pj_inv_mlfn(P->ctx, Q->M1 + xy.y - + x2 * tan(lp.phi) * (t = sqrt(1. - t * t)), P->es, Q->en); + } + lp.lam = xy.x * t / cos(lp.phi); + return lp; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c; + double azi1, azi2, s12, x2, y2, lat1, lon1, lat2, lon2; + + if ((c = hypot(xy.x, xy.y)) < EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + return (lp); + } + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + + x2 = xy.x * P->a; + y2 = xy.y * P->a; + lat1 = P->phi0 / DEG_TO_RAD; + lon1 = P->lam0 / DEG_TO_RAD; + azi1 = atan2(x2, y2) / DEG_TO_RAD; + s12 = sqrt(x2 * x2 + y2 * y2); + geod_direct(&Q->g, lat1, lon1, azi1, s12, &lat2, &lon2, &azi2); + lp.phi = lat2 * DEG_TO_RAD; + lp.lam = lon2 * DEG_TO_RAD; + lp.lam -= P->lam0; + } else { /* Polar */ + lp.phi = pj_inv_mlfn(P->ctx, Q->mode == N_POLE ? Q->Mp - c : Q->Mp + c, + P->es, Q->en); + lp.lam = atan2(xy.x, Q->mode == N_POLE ? -xy.y : xy.y); + } + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosc, c_rh, sinc; + + if ((c_rh = hypot(xy.x, xy.y)) > M_PI) { + if (c_rh - EPS10 > M_PI) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + c_rh = M_PI; + } else if (c_rh < EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + return (lp); + } + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinc = sin(c_rh); + cosc = cos(c_rh); + if (Q->mode == EQUIT) { + lp.phi = aasin(P->ctx, xy.y * sinc / c_rh); + xy.x *= sinc; + xy.y = cosc * c_rh; + } else { + lp.phi = aasin(P->ctx,cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 / + c_rh); + xy.y = (cosc - Q->sinph0 * sin(lp.phi)) * c_rh; + xy.x *= sinc * Q->cosph0; + } + lp.lam = xy.y == 0. ? 0. : atan2(xy.x, xy.y); + } else if (Q->mode == N_POLE) { + lp.phi = M_HALFPI - c_rh; + lp.lam = atan2(xy.x, -xy.y); + } else { + lp.phi = c_rh - M_HALFPI; + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +PJ *PROJECTION(aeqd) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + geod_init(&Q->g, P->a, P->es / (1 + sqrt(P->one_es))); + + if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) { + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + Q->sinph0 = P->phi0 < 0. ? -1. : 1.; + Q->cosph0 = 0.; + } else if (fabs(P->phi0) < EPS10) { + Q->mode = EQUIT; + Q->sinph0 = 0.; + Q->cosph0 = 1.; + } else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + if (P->es == 0.0) { + P->inv = s_inverse; + P->fwd = s_forward; + } else { + if (!(Q->en = pj_enfn(P->es))) + return pj_default_destructor (P, 0); + if (pj_param(P->ctx, P->params, "bguam").i) { + Q->M1 = pj_mlfn(P->phi0, Q->sinph0, Q->cosph0, Q->en); + P->inv = e_guam_inv; + P->fwd = e_guam_fwd; + } else { + switch (Q->mode) { + case N_POLE: + Q->Mp = pj_mlfn(M_HALFPI, 1., 0., Q->en); + break; + case S_POLE: + Q->Mp = pj_mlfn(-M_HALFPI, -1., 0., Q->en); + break; + case EQUIT: + case OBLIQ: + P->inv = e_inverse; P->fwd = e_forward; + Q->N1 = 1. / sqrt(1. - P->es * Q->sinph0 * Q->sinph0); + Q->G = Q->sinph0 * (Q->He = P->e / sqrt(P->one_es)); + Q->He *= Q->cosph0; + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } + } + + return P; +} + + diff --git a/src/PJ_affine.c b/src/PJ_affine.c deleted file mode 100644 index 0d8b6374..00000000 --- a/src/PJ_affine.c +++ /dev/null @@ -1,246 +0,0 @@ -/************************************************************************ -* Copyright (c) 2018, Even Rouault -* -* 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. -* -***********************************************************************/ -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(affine, "Affine transformation"); -PROJ_HEAD(geogoffset, "Geographic Offset"); - -struct pj_affine_coeffs { - double s11; - double s12; - double s13; - double s21; - double s22; - double s23; - double s31; - double s32; - double s33; - double tscale; -}; - -struct pj_opaque_affine { - double xoff; - double yoff; - double zoff; - double toff; - struct pj_affine_coeffs forward; - struct pj_affine_coeffs reverse; -}; - - -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - PJ_COORD newObs; - const struct pj_opaque_affine *Q = (const struct pj_opaque_affine *) P->opaque; - const struct pj_affine_coeffs *C = &(Q->forward); - newObs.xyzt.x = Q->xoff + C->s11 * obs.xyzt.x + C->s12 * obs.xyzt.y + C->s13 * obs.xyzt.z; - newObs.xyzt.y = Q->yoff + C->s21 * obs.xyzt.x + C->s22 * obs.xyzt.y + C->s23 * obs.xyzt.z; - newObs.xyzt.z = Q->zoff + C->s31 * obs.xyzt.x + C->s32 * obs.xyzt.y + C->s33 * obs.xyzt.z; - newObs.xyzt.t = Q->toff + C->tscale * obs.xyzt.t; - return newObs; -} - -static XYZ forward_3d(LPZ lpz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.lpz = lpz; - return forward_4d(point, P).xyz; -} - - -static XY forward_2d(LP lp, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.lp = lp; - return forward_4d(point, P).xy; -} - - -static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - PJ_COORD newObs; - const struct pj_opaque_affine *Q = (const struct pj_opaque_affine *) P->opaque; - const struct pj_affine_coeffs *C = &(Q->reverse); - obs.xyzt.x -= Q->xoff; - obs.xyzt.y -= Q->yoff; - obs.xyzt.z -= Q->zoff; - newObs.xyzt.x = C->s11 * obs.xyzt.x + C->s12 * obs.xyzt.y + C->s13 * obs.xyzt.z; - newObs.xyzt.y = C->s21 * obs.xyzt.x + C->s22 * obs.xyzt.y + C->s23 * obs.xyzt.z; - newObs.xyzt.z = C->s31 * obs.xyzt.x + C->s32 * obs.xyzt.y + C->s33 * obs.xyzt.z; - newObs.xyzt.t = C->tscale * (obs.xyzt.t - Q->toff); - return newObs; -} - -static LPZ reverse_3d(XYZ xyz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.xyz = xyz; - return reverse_4d(point, P).lpz; -} - -static LP reverse_2d(XY xy, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.xy = xy; - return reverse_4d(point, P).lp; -} - -static struct pj_opaque_affine * initQ() { - struct pj_opaque_affine *Q = pj_calloc(1, sizeof(struct pj_opaque_affine)); - if (0==Q) - return 0; - - /* default values */ - Q->forward.s11 = 1.0; - Q->forward.s22 = 1.0; - Q->forward.s33 = 1.0; - Q->forward.tscale = 1.0; - - Q->reverse.s11 = 1.0; - Q->reverse.s22 = 1.0; - Q->reverse.s33 = 1.0; - Q->reverse.tscale = 1.0; - - return Q; -} - -static void computeReverseParameters(PJ* P) -{ - struct pj_opaque_affine *Q = (struct pj_opaque_affine *) P->opaque; - - /* cf https://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3_%C3%97_3_matrices */ - const double a = Q->forward.s11; - const double b = Q->forward.s12; - const double c = Q->forward.s13; - const double d = Q->forward.s21; - const double e = Q->forward.s22; - const double f = Q->forward.s23; - const double g = Q->forward.s31; - const double h = Q->forward.s32; - const double i = Q->forward.s33; - const double A = e * i - f * h; - const double B = -(d * i - f * g); - const double C = (d * h - e * g); - const double D = -(b * i - c * h); - const double E = (a * i - c * g); - const double F = -(a * h - b * g); - const double G = b * f - c * e; - const double H = -(a * f - c * d); - const double I = a * e - b * d; - const double det = a * A + b * B + c * C; - if( det == 0.0 || Q->forward.tscale == 0.0 ) { - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - proj_log_debug(P, "Affine: matrix non invertible"); - } - P->inv4d = NULL; - P->inv3d = NULL; - P->inv = NULL; - } else { - Q->reverse.s11 = A / det; - Q->reverse.s12 = D / det; - Q->reverse.s13 = G / det; - Q->reverse.s21 = B / det; - Q->reverse.s22 = E / det; - Q->reverse.s23 = H / det; - Q->reverse.s31 = C / det; - Q->reverse.s32 = F / det; - Q->reverse.s33 = I / det; - Q->reverse.tscale = 1.0 / Q->forward.tscale; - } -} - -PJ *TRANSFORMATION(affine,0 /* no need for ellipsoid */) { - struct pj_opaque_affine *Q = initQ(); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = (void *) Q; - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = forward_2d; - P->inv = reverse_2d; - - P->left = PJ_IO_UNITS_WHATEVER; - P->right = PJ_IO_UNITS_WHATEVER; - - /* read args */ - Q->xoff = pj_param(P->ctx, P->params, "dxoff").f; - Q->yoff = pj_param(P->ctx, P->params, "dyoff").f; - Q->zoff = pj_param(P->ctx, P->params, "dzoff").f; - Q->toff = pj_param(P->ctx, P->params, "dtoff").f; - - if(pj_param (P->ctx, P->params, "ts11").i) { - Q->forward.s11 = pj_param(P->ctx, P->params, "ds11").f; - } - Q->forward.s12 = pj_param(P->ctx, P->params, "ds12").f; - Q->forward.s13 = pj_param(P->ctx, P->params, "ds13").f; - Q->forward.s21 = pj_param(P->ctx, P->params, "ds21").f; - if(pj_param (P->ctx, P->params, "ts22").i) { - Q->forward.s22 = pj_param(P->ctx, P->params, "ds22").f; - } - Q->forward.s23 = pj_param(P->ctx, P->params, "ds23").f; - Q->forward.s31 = pj_param(P->ctx, P->params, "ds31").f; - Q->forward.s32 = pj_param(P->ctx, P->params, "ds32").f; - if(pj_param (P->ctx, P->params, "ts33").i) { - Q->forward.s33 = pj_param(P->ctx, P->params, "ds33").f; - } - if(pj_param (P->ctx, P->params, "ttscale").i) { - Q->forward.tscale = pj_param(P->ctx, P->params, "dtscale").f; - } - - computeReverseParameters(P); - - return P; -} - - -/* Arcsecond to radians */ -#define ARCSEC_TO_RAD (DEG_TO_RAD / 3600.0) - - -PJ *TRANSFORMATION(geogoffset,0 /* no need for ellipsoid */) { - struct pj_opaque_affine *Q = initQ(); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = (void *) Q; - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = forward_2d; - P->inv = reverse_2d; - - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - - /* read args */ - Q->xoff = pj_param(P->ctx, P->params, "ddlon").f * ARCSEC_TO_RAD; - Q->yoff = pj_param(P->ctx, P->params, "ddlat").f * ARCSEC_TO_RAD; - Q->zoff = pj_param(P->ctx, P->params, "ddh").f; - - return P; -} diff --git a/src/PJ_affine.cpp b/src/PJ_affine.cpp new file mode 100644 index 00000000..b8fa4c68 --- /dev/null +++ b/src/PJ_affine.cpp @@ -0,0 +1,246 @@ +/************************************************************************ +* Copyright (c) 2018, Even Rouault +* +* 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. +* +***********************************************************************/ +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(affine, "Affine transformation"); +PROJ_HEAD(geogoffset, "Geographic Offset"); + +struct pj_affine_coeffs { + double s11; + double s12; + double s13; + double s21; + double s22; + double s23; + double s31; + double s32; + double s33; + double tscale; +}; + +struct pj_opaque_affine { + double xoff; + double yoff; + double zoff; + double toff; + struct pj_affine_coeffs forward; + struct pj_affine_coeffs reverse; +}; + + +static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { + PJ_COORD newObs; + const struct pj_opaque_affine *Q = (const struct pj_opaque_affine *) P->opaque; + const struct pj_affine_coeffs *C = &(Q->forward); + newObs.xyzt.x = Q->xoff + C->s11 * obs.xyzt.x + C->s12 * obs.xyzt.y + C->s13 * obs.xyzt.z; + newObs.xyzt.y = Q->yoff + C->s21 * obs.xyzt.x + C->s22 * obs.xyzt.y + C->s23 * obs.xyzt.z; + newObs.xyzt.z = Q->zoff + C->s31 * obs.xyzt.x + C->s32 * obs.xyzt.y + C->s33 * obs.xyzt.z; + newObs.xyzt.t = Q->toff + C->tscale * obs.xyzt.t; + return newObs; +} + +static XYZ forward_3d(LPZ lpz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.lpz = lpz; + return forward_4d(point, P).xyz; +} + + +static XY forward_2d(LP lp, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.lp = lp; + return forward_4d(point, P).xy; +} + + +static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { + PJ_COORD newObs; + const struct pj_opaque_affine *Q = (const struct pj_opaque_affine *) P->opaque; + const struct pj_affine_coeffs *C = &(Q->reverse); + obs.xyzt.x -= Q->xoff; + obs.xyzt.y -= Q->yoff; + obs.xyzt.z -= Q->zoff; + newObs.xyzt.x = C->s11 * obs.xyzt.x + C->s12 * obs.xyzt.y + C->s13 * obs.xyzt.z; + newObs.xyzt.y = C->s21 * obs.xyzt.x + C->s22 * obs.xyzt.y + C->s23 * obs.xyzt.z; + newObs.xyzt.z = C->s31 * obs.xyzt.x + C->s32 * obs.xyzt.y + C->s33 * obs.xyzt.z; + newObs.xyzt.t = C->tscale * (obs.xyzt.t - Q->toff); + return newObs; +} + +static LPZ reverse_3d(XYZ xyz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.xyz = xyz; + return reverse_4d(point, P).lpz; +} + +static LP reverse_2d(XY xy, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.xy = xy; + return reverse_4d(point, P).lp; +} + +static struct pj_opaque_affine * initQ() { + struct pj_opaque_affine *Q = static_cast(pj_calloc(1, sizeof(struct pj_opaque_affine))); + if (0==Q) + return 0; + + /* default values */ + Q->forward.s11 = 1.0; + Q->forward.s22 = 1.0; + Q->forward.s33 = 1.0; + Q->forward.tscale = 1.0; + + Q->reverse.s11 = 1.0; + Q->reverse.s22 = 1.0; + Q->reverse.s33 = 1.0; + Q->reverse.tscale = 1.0; + + return Q; +} + +static void computeReverseParameters(PJ* P) +{ + struct pj_opaque_affine *Q = (struct pj_opaque_affine *) P->opaque; + + /* cf https://en.wikipedia.org/wiki/Invertible_matrix#Inversion_of_3_%C3%97_3_matrices */ + const double a = Q->forward.s11; + const double b = Q->forward.s12; + const double c = Q->forward.s13; + const double d = Q->forward.s21; + const double e = Q->forward.s22; + const double f = Q->forward.s23; + const double g = Q->forward.s31; + const double h = Q->forward.s32; + const double i = Q->forward.s33; + const double A = e * i - f * h; + const double B = -(d * i - f * g); + const double C = (d * h - e * g); + const double D = -(b * i - c * h); + const double E = (a * i - c * g); + const double F = -(a * h - b * g); + const double G = b * f - c * e; + const double H = -(a * f - c * d); + const double I = a * e - b * d; + const double det = a * A + b * B + c * C; + if( det == 0.0 || Q->forward.tscale == 0.0 ) { + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + proj_log_debug(P, "Affine: matrix non invertible"); + } + P->inv4d = NULL; + P->inv3d = NULL; + P->inv = NULL; + } else { + Q->reverse.s11 = A / det; + Q->reverse.s12 = D / det; + Q->reverse.s13 = G / det; + Q->reverse.s21 = B / det; + Q->reverse.s22 = E / det; + Q->reverse.s23 = H / det; + Q->reverse.s31 = C / det; + Q->reverse.s32 = F / det; + Q->reverse.s33 = I / det; + Q->reverse.tscale = 1.0 / Q->forward.tscale; + } +} + +PJ *TRANSFORMATION(affine,0 /* no need for ellipsoid */) { + struct pj_opaque_affine *Q = initQ(); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = (void *) Q; + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = forward_2d; + P->inv = reverse_2d; + + P->left = PJ_IO_UNITS_WHATEVER; + P->right = PJ_IO_UNITS_WHATEVER; + + /* read args */ + Q->xoff = pj_param(P->ctx, P->params, "dxoff").f; + Q->yoff = pj_param(P->ctx, P->params, "dyoff").f; + Q->zoff = pj_param(P->ctx, P->params, "dzoff").f; + Q->toff = pj_param(P->ctx, P->params, "dtoff").f; + + if(pj_param (P->ctx, P->params, "ts11").i) { + Q->forward.s11 = pj_param(P->ctx, P->params, "ds11").f; + } + Q->forward.s12 = pj_param(P->ctx, P->params, "ds12").f; + Q->forward.s13 = pj_param(P->ctx, P->params, "ds13").f; + Q->forward.s21 = pj_param(P->ctx, P->params, "ds21").f; + if(pj_param (P->ctx, P->params, "ts22").i) { + Q->forward.s22 = pj_param(P->ctx, P->params, "ds22").f; + } + Q->forward.s23 = pj_param(P->ctx, P->params, "ds23").f; + Q->forward.s31 = pj_param(P->ctx, P->params, "ds31").f; + Q->forward.s32 = pj_param(P->ctx, P->params, "ds32").f; + if(pj_param (P->ctx, P->params, "ts33").i) { + Q->forward.s33 = pj_param(P->ctx, P->params, "ds33").f; + } + if(pj_param (P->ctx, P->params, "ttscale").i) { + Q->forward.tscale = pj_param(P->ctx, P->params, "dtscale").f; + } + + computeReverseParameters(P); + + return P; +} + + +/* Arcsecond to radians */ +#define ARCSEC_TO_RAD (DEG_TO_RAD / 3600.0) + + +PJ *TRANSFORMATION(geogoffset,0 /* no need for ellipsoid */) { + struct pj_opaque_affine *Q = initQ(); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = (void *) Q; + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = forward_2d; + P->inv = reverse_2d; + + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + + /* read args */ + Q->xoff = pj_param(P->ctx, P->params, "ddlon").f * ARCSEC_TO_RAD; + Q->yoff = pj_param(P->ctx, P->params, "ddlat").f * ARCSEC_TO_RAD; + Q->zoff = pj_param(P->ctx, P->params, "ddh").f; + + return P; +} diff --git a/src/PJ_airy.c b/src/PJ_airy.c deleted file mode 100644 index 75f95780..00000000 --- a/src/PJ_airy.c +++ /dev/null @@ -1,151 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the airy (Airy) projection. - * Author: Gerald Evenden (1995) - * Thomas Knudsen (2016) - revise/add regression tests - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ -#include "proj.h" -#include -#include "projects.h" - -PROJ_HEAD(airy, "Airy") "\n\tMisc Sph, no inv\n\tno_cut lat_b="; - - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double p_halfpi; - double sinph0; - double cosph0; - double Cb; - enum Mode mode; - int no_cut; /* do not cut at hemisphere limit */ -}; - - -# define EPS 1.e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sinlam, coslam, cosphi, sinphi, t, s, Krho, cosz; - - sinlam = sin(lp.lam); - coslam = cos(lp.lam); - switch (Q->mode) { - case EQUIT: - case OBLIQ: - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - cosz = cosphi * coslam; - if (Q->mode == OBLIQ) - cosz = Q->sinph0 * sinphi + Q->cosph0 * cosz; - if (!Q->no_cut && cosz < -EPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - if (fabs(s = 1. - cosz) > EPS) { - t = 0.5 * (1. + cosz); - Krho = -log(t)/s - Q->Cb / t; - } else - Krho = 0.5 - Q->Cb; - xy.x = Krho * cosphi * sinlam; - if (Q->mode == OBLIQ) - xy.y = Krho * (Q->cosph0 * sinphi - - Q->sinph0 * cosphi * coslam); - else - xy.y = Krho * sinphi; - break; - case S_POLE: - case N_POLE: - lp.phi = fabs(Q->p_halfpi - lp.phi); - if (!Q->no_cut && (lp.phi - EPS) > M_HALFPI) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - if ((lp.phi *= 0.5) > EPS) { - t = tan(lp.phi); - Krho = -2.*(log(cos(lp.phi)) / t + t * Q->Cb); - xy.x = Krho * sinlam; - xy.y = Krho * coslam; - if (Q->mode == N_POLE) - xy.y = -xy.y; - } else - xy.x = xy.y = 0.; - } - return xy; -} - - - - -PJ *PROJECTION(airy) { - double beta; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - - P->opaque = Q; - - Q->no_cut = pj_param(P->ctx, P->params, "bno_cut").i; - beta = 0.5 * (M_HALFPI - pj_param(P->ctx, P->params, "rlat_b").f); - if (fabs(beta) < EPS) - Q->Cb = -0.5; - else { - Q->Cb = 1./tan(beta); - Q->Cb *= Q->Cb * log(cos(beta)); - } - - if (fabs(fabs(P->phi0) - M_HALFPI) < EPS) - if (P->phi0 < 0.) { - Q->p_halfpi = -M_HALFPI; - Q->mode = S_POLE; - } else { - Q->p_halfpi = M_HALFPI; - Q->mode = N_POLE; - } - else { - if (fabs(P->phi0) < EPS) - Q->mode = EQUIT; - else { - Q->mode = OBLIQ; - Q->sinph0 = sin(P->phi0); - Q->cosph0 = cos(P->phi0); - } - } - P->fwd = s_forward; - P->es = 0.; - return P; -} - - diff --git a/src/PJ_airy.cpp b/src/PJ_airy.cpp new file mode 100644 index 00000000..037362b3 --- /dev/null +++ b/src/PJ_airy.cpp @@ -0,0 +1,151 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the airy (Airy) projection. + * Author: Gerald Evenden (1995) + * Thomas Knudsen (2016) - revise/add regression tests + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ +#include "proj.h" +#include +#include "projects.h" + +PROJ_HEAD(airy, "Airy") "\n\tMisc Sph, no inv\n\tno_cut lat_b="; + + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double p_halfpi; + double sinph0; + double cosph0; + double Cb; + enum Mode mode; + int no_cut; /* do not cut at hemisphere limit */ +}; + + +# define EPS 1.e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sinlam, coslam, cosphi, sinphi, t, s, Krho, cosz; + + sinlam = sin(lp.lam); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + case OBLIQ: + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + cosz = cosphi * coslam; + if (Q->mode == OBLIQ) + cosz = Q->sinph0 * sinphi + Q->cosph0 * cosz; + if (!Q->no_cut && cosz < -EPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + if (fabs(s = 1. - cosz) > EPS) { + t = 0.5 * (1. + cosz); + Krho = -log(t)/s - Q->Cb / t; + } else + Krho = 0.5 - Q->Cb; + xy.x = Krho * cosphi * sinlam; + if (Q->mode == OBLIQ) + xy.y = Krho * (Q->cosph0 * sinphi - + Q->sinph0 * cosphi * coslam); + else + xy.y = Krho * sinphi; + break; + case S_POLE: + case N_POLE: + lp.phi = fabs(Q->p_halfpi - lp.phi); + if (!Q->no_cut && (lp.phi - EPS) > M_HALFPI) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + if ((lp.phi *= 0.5) > EPS) { + t = tan(lp.phi); + Krho = -2.*(log(cos(lp.phi)) / t + t * Q->Cb); + xy.x = Krho * sinlam; + xy.y = Krho * coslam; + if (Q->mode == N_POLE) + xy.y = -xy.y; + } else + xy.x = xy.y = 0.; + } + return xy; +} + + + + +PJ *PROJECTION(airy) { + double beta; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + + P->opaque = Q; + + Q->no_cut = pj_param(P->ctx, P->params, "bno_cut").i; + beta = 0.5 * (M_HALFPI - pj_param(P->ctx, P->params, "rlat_b").f); + if (fabs(beta) < EPS) + Q->Cb = -0.5; + else { + Q->Cb = 1./tan(beta); + Q->Cb *= Q->Cb * log(cos(beta)); + } + + if (fabs(fabs(P->phi0) - M_HALFPI) < EPS) + if (P->phi0 < 0.) { + Q->p_halfpi = -M_HALFPI; + Q->mode = S_POLE; + } else { + Q->p_halfpi = M_HALFPI; + Q->mode = N_POLE; + } + else { + if (fabs(P->phi0) < EPS) + Q->mode = EQUIT; + else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + } + P->fwd = s_forward; + P->es = 0.; + return P; +} + + diff --git a/src/PJ_aitoff.c b/src/PJ_aitoff.c deleted file mode 100644 index 82c981d5..00000000 --- a/src/PJ_aitoff.c +++ /dev/null @@ -1,197 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the aitoff (Aitoff) and wintri (Winkel Tripel) - * projections. - * Author: Gerald Evenden (1995) - * Drazen Tutic, Lovro Gradiser (2015) - add inverse - * Thomas Knudsen (2016) - revise/add regression tests - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - - -enum Mode { - AITOFF = 0, - WINKEL_TRIPEL = 1 -}; - -struct pj_opaque { - double cosphi1; - enum Mode mode; -}; - - -PROJ_HEAD(aitoff, "Aitoff") "\n\tMisc Sph"; -PROJ_HEAD(wintri, "Winkel Tripel") "\n\tMisc Sph\n\tlat_1"; - - - -#if 0 -FORWARD(s_forward); /* spheroid */ -#endif - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c, d; - - if((d = acos(cos(lp.phi) * cos(c = 0.5 * lp.lam))) != 0.0) {/* basic Aitoff */ - xy.x = 2. * d * cos(lp.phi) * sin(c) * (xy.y = 1. / sin(d)); - xy.y *= d * sin(lp.phi); - } else - xy.x = xy.y = 0.; - if (Q->mode == WINKEL_TRIPEL) { - xy.x = (xy.x + lp.lam * Q->cosphi1) * 0.5; - xy.y = (xy.y + lp.phi) * 0.5; - } - return (xy); -} - -/*********************************************************************************** -* -* Inverse functions added by Drazen Tutic and Lovro Gradiser based on paper: -* -* I.Özbug Biklirici and Cengizhan Ipbüker. A General Algorithm for the Inverse -* Transformation of Map Projections Using Jacobian Matrices. In Proceedings of the -* Third International Symposium Mathematical & Computational Applications, -* pages 175{182, Turkey, September 2002. -* -* Expected accuracy is defined by EPSILON = 1e-12. Should be appropriate for -* most applications of Aitoff and Winkel Tripel projections. -* -* Longitudes of 180W and 180E can be mixed in solution obtained. -* -* Inverse for Aitoff projection in poles is undefined, longitude value of 0 is assumed. -* -* Contact : dtutic@geof.hr -* Date: 2015-02-16 -* -************************************************************************************/ - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int iter, MAXITER = 10, round = 0, MAXROUND = 20; - double EPSILON = 1e-12, D, C, f1, f2, f1p, f1l, f2p, f2l, dp, dl, sl, sp, cp, cl, x, y; - - if ((fabs(xy.x) < EPSILON) && (fabs(xy.y) < EPSILON )) { lp.phi = 0.; lp.lam = 0.; return lp; } - - /* initial values for Newton-Raphson method */ - lp.phi = xy.y; lp.lam = xy.x; - do { - iter = 0; - do { - sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5); - sp = sin(lp.phi); cp = cos(lp.phi); - D = cp * cl; - C = 1. - D * D; - D = acos(D) / pow(C, 1.5); - f1 = 2. * D * C * cp * sl; - f2 = D * C * sp; - f1p = 2.* (sl * cl * sp * cp / C - D * sp * sl); - f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp; - f2p = sp * sp * cl / C + D * sl * sl * cp; - f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl); - if (Q->mode == WINKEL_TRIPEL) { - f1 = 0.5 * (f1 + lp.lam * Q->cosphi1); - f2 = 0.5 * (f2 + lp.phi); - f1p *= 0.5; - f1l = 0.5 * (f1l + Q->cosphi1); - f2p = 0.5 * (f2p + 1.); - f2l *= 0.5; - } - f1 -= xy.x; f2 -= xy.y; - dl = (f2 * f1p - f1 * f2p) / (dp = f1p * f2l - f2p * f1l); - dp = (f1 * f2l - f2 * f1l) / dp; - dl = fmod(dl, M_PI); /* set to interval [-M_PI, M_PI] */ - lp.phi -= dp; lp.lam -= dl; - } while ((fabs(dp) > EPSILON || fabs(dl) > EPSILON) && (iter++ < MAXITER)); - if (lp.phi > M_PI_2) lp.phi -= 2.*(lp.phi-M_PI_2); /* correct if symmetrical solution for Aitoff */ - if (lp.phi < -M_PI_2) lp.phi -= 2.*(lp.phi+M_PI_2); /* correct if symmetrical solution for Aitoff */ - if ((fabs(fabs(lp.phi) - M_PI_2) < EPSILON) && (Q->mode == AITOFF)) lp.lam = 0.; /* if pole in Aitoff, return longitude of 0 */ - - /* calculate x,y coordinates with solution obtained */ - if((D = acos(cos(lp.phi) * cos(C = 0.5 * lp.lam))) != 0.0) {/* Aitoff */ - x = 2. * D * cos(lp.phi) * sin(C) * (y = 1. / sin(D)); - y *= D * sin(lp.phi); - } else - x = y = 0.; - if (Q->mode == WINKEL_TRIPEL) { - x = (x + lp.lam * Q->cosphi1) * 0.5; - y = (y + lp.phi) * 0.5; - } - /* if too far from given values of x,y, repeat with better approximation of phi,lam */ - } while (((fabs(xy.x-x) > EPSILON) || (fabs(xy.y-y) > EPSILON)) && (round++ < MAXROUND)); - - if (iter == MAXITER && round == MAXROUND) - { - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - /* fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); */ - } - - return lp; -} - - -static PJ *setup(PJ *P) { - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - return P; -} - - -PJ *PROJECTION(aitoff) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->mode = AITOFF; - return setup(P); -} - - -PJ *PROJECTION(wintri) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->mode = WINKEL_TRIPEL; - if (pj_param(P->ctx, P->params, "tlat_1").i) { - if ((Q->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f)) == 0.) - return pj_default_destructor (P, PJD_ERR_LAT_LARGER_THAN_90); - } - else /* 50d28' or acos(2/pi) */ - Q->cosphi1 = 0.636619772367581343; - return setup(P); -} diff --git a/src/PJ_aitoff.cpp b/src/PJ_aitoff.cpp new file mode 100644 index 00000000..1af75469 --- /dev/null +++ b/src/PJ_aitoff.cpp @@ -0,0 +1,197 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the aitoff (Aitoff) and wintri (Winkel Tripel) + * projections. + * Author: Gerald Evenden (1995) + * Drazen Tutic, Lovro Gradiser (2015) - add inverse + * Thomas Knudsen (2016) - revise/add regression tests + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + + +enum Mode { + AITOFF = 0, + WINKEL_TRIPEL = 1 +}; + +struct pj_opaque { + double cosphi1; + enum Mode mode; +}; + + +PROJ_HEAD(aitoff, "Aitoff") "\n\tMisc Sph"; +PROJ_HEAD(wintri, "Winkel Tripel") "\n\tMisc Sph\n\tlat_1"; + + + +#if 0 +FORWARD(s_forward); /* spheroid */ +#endif + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c, d; + + if((d = acos(cos(lp.phi) * cos(c = 0.5 * lp.lam))) != 0.0) {/* basic Aitoff */ + xy.x = 2. * d * cos(lp.phi) * sin(c) * (xy.y = 1. / sin(d)); + xy.y *= d * sin(lp.phi); + } else + xy.x = xy.y = 0.; + if (Q->mode == WINKEL_TRIPEL) { + xy.x = (xy.x + lp.lam * Q->cosphi1) * 0.5; + xy.y = (xy.y + lp.phi) * 0.5; + } + return (xy); +} + +/*********************************************************************************** +* +* Inverse functions added by Drazen Tutic and Lovro Gradiser based on paper: +* +* I.Özbug Biklirici and Cengizhan Ipbüker. A General Algorithm for the Inverse +* Transformation of Map Projections Using Jacobian Matrices. In Proceedings of the +* Third International Symposium Mathematical & Computational Applications, +* pages 175{182, Turkey, September 2002. +* +* Expected accuracy is defined by EPSILON = 1e-12. Should be appropriate for +* most applications of Aitoff and Winkel Tripel projections. +* +* Longitudes of 180W and 180E can be mixed in solution obtained. +* +* Inverse for Aitoff projection in poles is undefined, longitude value of 0 is assumed. +* +* Contact : dtutic@geof.hr +* Date: 2015-02-16 +* +************************************************************************************/ + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int iter, MAXITER = 10, round = 0, MAXROUND = 20; + double EPSILON = 1e-12, D, C, f1, f2, f1p, f1l, f2p, f2l, dp, dl, sl, sp, cp, cl, x, y; + + if ((fabs(xy.x) < EPSILON) && (fabs(xy.y) < EPSILON )) { lp.phi = 0.; lp.lam = 0.; return lp; } + + /* initial values for Newton-Raphson method */ + lp.phi = xy.y; lp.lam = xy.x; + do { + iter = 0; + do { + sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5); + sp = sin(lp.phi); cp = cos(lp.phi); + D = cp * cl; + C = 1. - D * D; + D = acos(D) / pow(C, 1.5); + f1 = 2. * D * C * cp * sl; + f2 = D * C * sp; + f1p = 2.* (sl * cl * sp * cp / C - D * sp * sl); + f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp; + f2p = sp * sp * cl / C + D * sl * sl * cp; + f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl); + if (Q->mode == WINKEL_TRIPEL) { + f1 = 0.5 * (f1 + lp.lam * Q->cosphi1); + f2 = 0.5 * (f2 + lp.phi); + f1p *= 0.5; + f1l = 0.5 * (f1l + Q->cosphi1); + f2p = 0.5 * (f2p + 1.); + f2l *= 0.5; + } + f1 -= xy.x; f2 -= xy.y; + dl = (f2 * f1p - f1 * f2p) / (dp = f1p * f2l - f2p * f1l); + dp = (f1 * f2l - f2 * f1l) / dp; + dl = fmod(dl, M_PI); /* set to interval [-M_PI, M_PI] */ + lp.phi -= dp; lp.lam -= dl; + } while ((fabs(dp) > EPSILON || fabs(dl) > EPSILON) && (iter++ < MAXITER)); + if (lp.phi > M_PI_2) lp.phi -= 2.*(lp.phi-M_PI_2); /* correct if symmetrical solution for Aitoff */ + if (lp.phi < -M_PI_2) lp.phi -= 2.*(lp.phi+M_PI_2); /* correct if symmetrical solution for Aitoff */ + if ((fabs(fabs(lp.phi) - M_PI_2) < EPSILON) && (Q->mode == AITOFF)) lp.lam = 0.; /* if pole in Aitoff, return longitude of 0 */ + + /* calculate x,y coordinates with solution obtained */ + if((D = acos(cos(lp.phi) * cos(C = 0.5 * lp.lam))) != 0.0) {/* Aitoff */ + x = 2. * D * cos(lp.phi) * sin(C) * (y = 1. / sin(D)); + y *= D * sin(lp.phi); + } else + x = y = 0.; + if (Q->mode == WINKEL_TRIPEL) { + x = (x + lp.lam * Q->cosphi1) * 0.5; + y = (y + lp.phi) * 0.5; + } + /* if too far from given values of x,y, repeat with better approximation of phi,lam */ + } while (((fabs(xy.x-x) > EPSILON) || (fabs(xy.y-y) > EPSILON)) && (round++ < MAXROUND)); + + if (iter == MAXITER && round == MAXROUND) + { + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + /* fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); */ + } + + return lp; +} + + +static PJ *setup(PJ *P) { + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} + + +PJ *PROJECTION(aitoff) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->mode = AITOFF; + return setup(P); +} + + +PJ *PROJECTION(wintri) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->mode = WINKEL_TRIPEL; + if (pj_param(P->ctx, P->params, "tlat_1").i) { + if ((Q->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f)) == 0.) + return pj_default_destructor (P, PJD_ERR_LAT_LARGER_THAN_90); + } + else /* 50d28' or acos(2/pi) */ + Q->cosphi1 = 0.636619772367581343; + return setup(P); +} diff --git a/src/PJ_august.c b/src/PJ_august.c deleted file mode 100644 index e891e84e..00000000 --- a/src/PJ_august.c +++ /dev/null @@ -1,34 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(august, "August Epicycloidal") "\n\tMisc Sph, no inv"; -#define M 1.333333333333333 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double t, c1, c, x1, x12, y1, y12; - (void) P; - - t = tan(.5 * lp.phi); - c1 = sqrt(1. - t * t); - c = 1. + c1 * cos(lp.lam *= .5); - x1 = sin(lp.lam) * c1 / c; - y1 = t / c; - xy.x = M * x1 * (3. + (x12 = x1 * x1) - 3. * (y12 = y1 * y1)); - xy.y = M * y1 * (3. + 3. * x12 - y12); - return (xy); -} - - - - -PJ *PROJECTION(august) { - P->inv = 0; - P->fwd = s_forward; - P->es = 0.; - return P; -} diff --git a/src/PJ_august.cpp b/src/PJ_august.cpp new file mode 100644 index 00000000..e891e84e --- /dev/null +++ b/src/PJ_august.cpp @@ -0,0 +1,34 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(august, "August Epicycloidal") "\n\tMisc Sph, no inv"; +#define M 1.333333333333333 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double t, c1, c, x1, x12, y1, y12; + (void) P; + + t = tan(.5 * lp.phi); + c1 = sqrt(1. - t * t); + c = 1. + c1 * cos(lp.lam *= .5); + x1 = sin(lp.lam) * c1 / c; + y1 = t / c; + xy.x = M * x1 * (3. + (x12 = x1 * x1) - 3. * (y12 = y1 * y1)); + xy.y = M * y1 * (3. + 3. * x12 - y12); + return (xy); +} + + + + +PJ *PROJECTION(august) { + P->inv = 0; + P->fwd = s_forward; + P->es = 0.; + return P; +} diff --git a/src/PJ_axisswap.c b/src/PJ_axisswap.c deleted file mode 100644 index 6db4a7d2..00000000 --- a/src/PJ_axisswap.c +++ /dev/null @@ -1,300 +0,0 @@ -/*********************************************************************** - - Axis order operation for use with transformation pipelines. - - Kristian Evers, kreve@sdfe.dk, 2017-10-31 - -************************************************************************ - -Change the order and sign of 2,3 or 4 axes. Each of the possible four -axes are numbered with 1-4, such that the first input axis is 1, the -second is 2 and so on. The output ordering is controlled by a list of the -input axes re-ordered to the new mapping. Examples: - -Reversing the order of the axes: - - +proj=axisswap +order=4,3,2,1 - -Swapping the first two axes (x and y): - - +proj=axisswap +order=2,1,3,4 - -The direction, or sign, of an axis can be changed by adding a minus in -front of the axis-number: - - +proj=axisswap +order=1,-2,3,4 - -It is only necessary to specify the axes that are affected by the swap -operation: - - +proj=axisswap +order=2,1 - -************************************************************************ -* Copyright (c) 2017, Kristian Evers / SDFE -* -* 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. -* -***********************************************************************/ - -#define PJ_LIB__ -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(axisswap, "Axis ordering"); - -struct pj_opaque { - unsigned int axis[4]; - int sign[4]; -}; - -static int sign(int x) { - return (x > 0) - (x < 0); -} - -static XY forward_2d(LP lp, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD out, in; - - in.lp = lp; - out = proj_coord_error(); - - for (i=0; i<2; i++) - out.v[i] = in.v[Q->axis[i]] * Q->sign[i]; - - return out.xy; -} - - -static LP reverse_2d(XY xy, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD out, in; - - in.xy = xy; - out = proj_coord_error(); - - for (i=0; i<2; i++) - out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; - - return out.lp; -} - - -static XYZ forward_3d(LPZ lpz, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD out, in; - - in.lpz = lpz; - out = proj_coord_error(); - - for (i=0; i<3; i++) - out.v[i] = in.v[Q->axis[i]] * Q->sign[i]; - - return out.xyz; -} - -static LPZ reverse_3d(XYZ xyz, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD in, out; - - out = proj_coord_error(); - in.xyz = xyz; - - for (i=0; i<3; i++) - out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; - - return out.lpz; -} - - -static PJ_COORD forward_4d(PJ_COORD coo, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD out; - - out = proj_coord_error(); - - for (i=0; i<4; i++) - out.v[i] = coo.v[Q->axis[i]] * Q->sign[i]; - - return out; -} - - -static PJ_COORD reverse_4d(PJ_COORD coo, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - unsigned int i; - PJ_COORD out; - - out = proj_coord_error(); - - for (i=0; i<4; i++) - out.v[Q->axis[i]] = coo.v[i] * Q->sign[i]; - - return out; -} - - -/***********************************************************************/ -PJ *CONVERSION(axisswap,0) { -/***********************************************************************/ - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - char *s; - unsigned int i, j, n = 0; - - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = (void *) Q; - - - /* +order and +axis are mutually exclusive */ - if ( !pj_param_exists(P->params, "order") == !pj_param_exists(P->params, "axis") ) - return pj_default_destructor(P, PJD_ERR_AXIS); - - /* fill axis list with indices from 4-7 to simplify duplicate search further down */ - for (i=0; i<4; i++) { - Q->axis[i] = i+4; - Q->sign[i] = 1; - } - - /* if the "order" parameter is used */ - if ( pj_param_exists(P->params, "order") ) { - /* read axis order */ - char *order = pj_param(P->ctx, P->params, "sorder").s; - - /* check that all characters are valid */ - for (i=0; iaxis[n] = abs(atoi(s))-1; - if (Q->axis[n] > 3) { - proj_log_error(P, "axisswap: invalid axis '%d'", Q->axis[n]); - return pj_default_destructor(P, PJD_ERR_AXIS); - } - Q->sign[n++] = sign(atoi(s)); - while ( *s != '\0' && *s != ',' ) - s++; - if ( *s == ',' ) - s++; - } - } - - /* if the "axis" parameter is used */ - if ( pj_param_exists(P->params, "axis") ) { - /* parse the classic PROJ.4 enu axis specification */ - for (i=0; i < 3; i++) { - switch(P->axis[i]) { - case 'w': - Q->sign[i] = -1; - Q->axis[i] = 0; - break; - case 'e': - Q->sign[i] = 1; - Q->axis[i] = 0; - break; - case 's': - Q->sign[i] = -1; - Q->axis[i] = 1; - break; - case 'n': - Q->sign[i] = 1; - Q->axis[i] = 1; - break; - case 'd': - Q->sign[i] = -1; - Q->axis[i] = 2; - break; - case 'u': - Q->sign[i] = 1; - Q->axis[i] = 2; - break; - default: - proj_log_error(P, "axisswap: unknown axis '%c'", P->axis[i]); - return pj_default_destructor(P, PJD_ERR_AXIS); - } - } - n = 3; - } - - /* check for duplicate axes */ - for (i=0; i<4; i++) - for (j=0; j<4; j++) { - if (i==j) - continue; - if (Q->axis[i] == Q->axis[j]) { - proj_log_error(P, "swapaxis: duplicate axes specified"); - return pj_default_destructor(P, PJD_ERR_AXIS); - } - } - - - /* only map fwd/inv functions that are possible with the given axis setup */ - if (n == 4) { - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - } - if (n == 3 && Q->axis[0] < 3 && Q->axis[1] < 3 && Q->axis[2] < 3) { - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - } - if (n == 2 && Q->axis[0] < 2 && Q->axis[1] < 2) { - P->fwd = forward_2d; - P->inv = reverse_2d; - } - - - if (P->fwd4d == NULL && P->fwd3d == NULL && P->fwd == NULL) { - proj_log_error(P, "swapaxis: bad axis order"); - return pj_default_destructor(P, PJD_ERR_AXIS); - } - - if (pj_param(P->ctx, P->params, "tangularunits").i) { - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - } else { - P->left = PJ_IO_UNITS_WHATEVER; - P->right = PJ_IO_UNITS_WHATEVER; - } - - - /* Preparation and finalization steps are skipped, since the raison */ - /* d'etre of axisswap is to bring input coordinates in line with the */ - /* the internally expected order (ENU), such that handling of offsets */ - /* etc. can be done correctly in a later step of a pipeline */ - P->skip_fwd_prepare = 1; - P->skip_fwd_finalize = 1; - P->skip_inv_prepare = 1; - P->skip_inv_finalize = 1; - - return P; -} diff --git a/src/PJ_axisswap.cpp b/src/PJ_axisswap.cpp new file mode 100644 index 00000000..d21eddb1 --- /dev/null +++ b/src/PJ_axisswap.cpp @@ -0,0 +1,300 @@ +/*********************************************************************** + + Axis order operation for use with transformation pipelines. + + Kristian Evers, kreve@sdfe.dk, 2017-10-31 + +************************************************************************ + +Change the order and sign of 2,3 or 4 axes. Each of the possible four +axes are numbered with 1-4, such that the first input axis is 1, the +second is 2 and so on. The output ordering is controlled by a list of the +input axes re-ordered to the new mapping. Examples: + +Reversing the order of the axes: + + +proj=axisswap +order=4,3,2,1 + +Swapping the first two axes (x and y): + + +proj=axisswap +order=2,1,3,4 + +The direction, or sign, of an axis can be changed by adding a minus in +front of the axis-number: + + +proj=axisswap +order=1,-2,3,4 + +It is only necessary to specify the axes that are affected by the swap +operation: + + +proj=axisswap +order=2,1 + +************************************************************************ +* Copyright (c) 2017, Kristian Evers / SDFE +* +* 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. +* +***********************************************************************/ + +#define PJ_LIB__ +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(axisswap, "Axis ordering"); + +struct pj_opaque { + unsigned int axis[4]; + int sign[4]; +}; + +static int sign(int x) { + return (x > 0) - (x < 0); +} + +static XY forward_2d(LP lp, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD out, in; + + in.lp = lp; + out = proj_coord_error(); + + for (i=0; i<2; i++) + out.v[i] = in.v[Q->axis[i]] * Q->sign[i]; + + return out.xy; +} + + +static LP reverse_2d(XY xy, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD out, in; + + in.xy = xy; + out = proj_coord_error(); + + for (i=0; i<2; i++) + out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; + + return out.lp; +} + + +static XYZ forward_3d(LPZ lpz, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD out, in; + + in.lpz = lpz; + out = proj_coord_error(); + + for (i=0; i<3; i++) + out.v[i] = in.v[Q->axis[i]] * Q->sign[i]; + + return out.xyz; +} + +static LPZ reverse_3d(XYZ xyz, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD in, out; + + out = proj_coord_error(); + in.xyz = xyz; + + for (i=0; i<3; i++) + out.v[Q->axis[i]] = in.v[i] * Q->sign[i]; + + return out.lpz; +} + + +static PJ_COORD forward_4d(PJ_COORD coo, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD out; + + out = proj_coord_error(); + + for (i=0; i<4; i++) + out.v[i] = coo.v[Q->axis[i]] * Q->sign[i]; + + return out; +} + + +static PJ_COORD reverse_4d(PJ_COORD coo, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + unsigned int i; + PJ_COORD out; + + out = proj_coord_error(); + + for (i=0; i<4; i++) + out.v[Q->axis[i]] = coo.v[i] * Q->sign[i]; + + return out; +} + + +/***********************************************************************/ +PJ *CONVERSION(axisswap,0) { +/***********************************************************************/ + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + char *s; + unsigned int i, j, n = 0; + + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = (void *) Q; + + + /* +order and +axis are mutually exclusive */ + if ( !pj_param_exists(P->params, "order") == !pj_param_exists(P->params, "axis") ) + return pj_default_destructor(P, PJD_ERR_AXIS); + + /* fill axis list with indices from 4-7 to simplify duplicate search further down */ + for (i=0; i<4; i++) { + Q->axis[i] = i+4; + Q->sign[i] = 1; + } + + /* if the "order" parameter is used */ + if ( pj_param_exists(P->params, "order") ) { + /* read axis order */ + char *order = pj_param(P->ctx, P->params, "sorder").s; + + /* check that all characters are valid */ + for (i=0; iaxis[n] = abs(atoi(s))-1; + if (Q->axis[n] > 3) { + proj_log_error(P, "axisswap: invalid axis '%d'", Q->axis[n]); + return pj_default_destructor(P, PJD_ERR_AXIS); + } + Q->sign[n++] = sign(atoi(s)); + while ( *s != '\0' && *s != ',' ) + s++; + if ( *s == ',' ) + s++; + } + } + + /* if the "axis" parameter is used */ + if ( pj_param_exists(P->params, "axis") ) { + /* parse the classic PROJ.4 enu axis specification */ + for (i=0; i < 3; i++) { + switch(P->axis[i]) { + case 'w': + Q->sign[i] = -1; + Q->axis[i] = 0; + break; + case 'e': + Q->sign[i] = 1; + Q->axis[i] = 0; + break; + case 's': + Q->sign[i] = -1; + Q->axis[i] = 1; + break; + case 'n': + Q->sign[i] = 1; + Q->axis[i] = 1; + break; + case 'd': + Q->sign[i] = -1; + Q->axis[i] = 2; + break; + case 'u': + Q->sign[i] = 1; + Q->axis[i] = 2; + break; + default: + proj_log_error(P, "axisswap: unknown axis '%c'", P->axis[i]); + return pj_default_destructor(P, PJD_ERR_AXIS); + } + } + n = 3; + } + + /* check for duplicate axes */ + for (i=0; i<4; i++) + for (j=0; j<4; j++) { + if (i==j) + continue; + if (Q->axis[i] == Q->axis[j]) { + proj_log_error(P, "swapaxis: duplicate axes specified"); + return pj_default_destructor(P, PJD_ERR_AXIS); + } + } + + + /* only map fwd/inv functions that are possible with the given axis setup */ + if (n == 4) { + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + } + if (n == 3 && Q->axis[0] < 3 && Q->axis[1] < 3 && Q->axis[2] < 3) { + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + } + if (n == 2 && Q->axis[0] < 2 && Q->axis[1] < 2) { + P->fwd = forward_2d; + P->inv = reverse_2d; + } + + + if (P->fwd4d == NULL && P->fwd3d == NULL && P->fwd == NULL) { + proj_log_error(P, "swapaxis: bad axis order"); + return pj_default_destructor(P, PJD_ERR_AXIS); + } + + if (pj_param(P->ctx, P->params, "tangularunits").i) { + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + } else { + P->left = PJ_IO_UNITS_WHATEVER; + P->right = PJ_IO_UNITS_WHATEVER; + } + + + /* Preparation and finalization steps are skipped, since the raison */ + /* d'etre of axisswap is to bring input coordinates in line with the */ + /* the internally expected order (ENU), such that handling of offsets */ + /* etc. can be done correctly in a later step of a pipeline */ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + + return P; +} diff --git a/src/PJ_bacon.c b/src/PJ_bacon.c deleted file mode 100644 index f0f11c9d..00000000 --- a/src/PJ_bacon.c +++ /dev/null @@ -1,79 +0,0 @@ -# define HLFPI2 2.46740110027233965467 /* (pi/2)^2 */ -# define EPS 1e-10 -#define PJ_LIB__ -#include -#include - -#include "projects.h" - - -struct pj_opaque { - int bacn; - int ortl; -}; - -PROJ_HEAD(apian, "Apian Globular I") "\n\tMisc Sph, no inv"; -PROJ_HEAD(ortel, "Ortelius Oval") "\n\tMisc Sph, no inv"; -PROJ_HEAD(bacon, "Bacon Globular") "\n\tMisc Sph, no inv"; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double ax, f; - - xy.y = Q->bacn ? M_HALFPI * sin(lp.phi) : lp.phi; - if ((ax = fabs(lp.lam)) >= EPS) { - if (Q->ortl && ax >= M_HALFPI) - xy.x = sqrt(HLFPI2 - lp.phi * lp.phi + EPS) + ax - M_HALFPI; - else { - f = 0.5 * (HLFPI2 / ax + ax); - xy.x = ax - f + sqrt(f * f - xy.y * xy.y); - } - if (lp.lam < 0.) xy.x = - xy.x; - } else - xy.x = 0.; - return (xy); -} - - - -PJ *PROJECTION(bacon) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->bacn = 1; - Q->ortl = 0; - P->es = 0.; - P->fwd = s_forward; - return P; -} - - -PJ *PROJECTION(apian) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->bacn = Q->ortl = 0; - P->es = 0.; - P->fwd = s_forward; - return P; -} - - -PJ *PROJECTION(ortel) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->bacn = 0; - Q->ortl = 1; - P->es = 0.; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_bacon.cpp b/src/PJ_bacon.cpp new file mode 100644 index 00000000..f995e420 --- /dev/null +++ b/src/PJ_bacon.cpp @@ -0,0 +1,79 @@ +# define HLFPI2 2.46740110027233965467 /* (pi/2)^2 */ +# define EPS 1e-10 +#define PJ_LIB__ +#include +#include + +#include "projects.h" + + +struct pj_opaque { + int bacn; + int ortl; +}; + +PROJ_HEAD(apian, "Apian Globular I") "\n\tMisc Sph, no inv"; +PROJ_HEAD(ortel, "Ortelius Oval") "\n\tMisc Sph, no inv"; +PROJ_HEAD(bacon, "Bacon Globular") "\n\tMisc Sph, no inv"; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double ax, f; + + xy.y = Q->bacn ? M_HALFPI * sin(lp.phi) : lp.phi; + if ((ax = fabs(lp.lam)) >= EPS) { + if (Q->ortl && ax >= M_HALFPI) + xy.x = sqrt(HLFPI2 - lp.phi * lp.phi + EPS) + ax - M_HALFPI; + else { + f = 0.5 * (HLFPI2 / ax + ax); + xy.x = ax - f + sqrt(f * f - xy.y * xy.y); + } + if (lp.lam < 0.) xy.x = - xy.x; + } else + xy.x = 0.; + return (xy); +} + + + +PJ *PROJECTION(bacon) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->bacn = 1; + Q->ortl = 0; + P->es = 0.; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(apian) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->bacn = Q->ortl = 0; + P->es = 0.; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(ortel) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->bacn = 0; + Q->ortl = 1; + P->es = 0.; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_bertin1953.c b/src/PJ_bertin1953.c deleted file mode 100644 index 5a027da2..00000000 --- a/src/PJ_bertin1953.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - Created by Jacques Bertin in 1953, this projection was the go-to choice - of the French cartographic school when they wished to represent phenomena - on a global scale. - - Formula designed by Philippe Rivière, 2017. - https://visionscarto.net/bertin-projection-1953 - - Port to PROJ by Philippe Rivière, 21 September 2018 -*/ - -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(bertin1953, "Bertin 1953") - "\n\tMisc Sph no inv."; - -struct pj_opaque { - double cos_delta_phi, sin_delta_phi, cos_delta_gamma, sin_delta_gamma, deltaLambda; -}; - - -static XY s_forward (LP lp, PJ *P) { - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - double fu = 1.4, k = 12., w = 1.68, d; - - /* Rotate */ - double cosphi, x, y, z, z0; - lp.lam += PJ_TORAD(-16.5); - cosphi = cos(lp.phi); - x = cos(lp.lam) * cosphi; - y = sin(lp.lam) * cosphi; - z = sin(lp.phi); - z0 = z * Q->cos_delta_phi + x * Q->sin_delta_phi; - lp.lam = atan2(y * Q->cos_delta_gamma - z0 * Q->sin_delta_gamma, - x * Q->cos_delta_phi - z * Q->sin_delta_phi); - z0 = z0 * Q->cos_delta_gamma + y * Q->sin_delta_gamma; - lp.phi = asin(z0); - - lp.lam = adjlon(lp.lam); - - /* Adjust pre-projection */ - if (lp.lam + lp.phi < -fu) { - d = (lp.lam - lp.phi + 1.6) * (lp.lam + lp.phi + fu) / 8.; - lp.lam += d; - lp.phi -= 0.8 * d * sin(lp.phi + M_PI / 2.); - } - - /* Project with Hammer (1.68,2) */ - cosphi = cos(lp.phi); - d = sqrt(2./(1. + cosphi * cos(lp.lam / 2.))); - xy.x = w * d * cosphi * sin(lp.lam / 2.); - xy.y = d * sin(lp.phi); - - /* Adjust post-projection */ - d = (1. - cos(lp.lam * lp.phi)) / k; - if (xy.y < 0.) { - xy.x *= 1. + d; - } - if (xy.y > 0.) { - xy.x *= 1. + d / 1.5 * xy.x * xy.x; - } - - return xy; -} - - -PJ *PROJECTION(bertin1953) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - P->lam0 = 0; - P->phi0 = PJ_TORAD(-42.); - - Q->cos_delta_phi = cos(P->phi0); - Q->sin_delta_phi = sin(P->phi0); - Q->cos_delta_gamma = 1.; - Q->sin_delta_gamma = 0.; - - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_bertin1953.cpp b/src/PJ_bertin1953.cpp new file mode 100644 index 00000000..46420314 --- /dev/null +++ b/src/PJ_bertin1953.cpp @@ -0,0 +1,94 @@ +/* + Created by Jacques Bertin in 1953, this projection was the go-to choice + of the French cartographic school when they wished to represent phenomena + on a global scale. + + Formula designed by Philippe Rivière, 2017. + https://visionscarto.net/bertin-projection-1953 + + Port to PROJ by Philippe Rivière, 21 September 2018 +*/ + +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(bertin1953, "Bertin 1953") + "\n\tMisc Sph no inv."; + +struct pj_opaque { + double cos_delta_phi, sin_delta_phi, cos_delta_gamma, sin_delta_gamma, deltaLambda; +}; + + +static XY s_forward (LP lp, PJ *P) { + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + double fu = 1.4, k = 12., w = 1.68, d; + + /* Rotate */ + double cosphi, x, y, z, z0; + lp.lam += PJ_TORAD(-16.5); + cosphi = cos(lp.phi); + x = cos(lp.lam) * cosphi; + y = sin(lp.lam) * cosphi; + z = sin(lp.phi); + z0 = z * Q->cos_delta_phi + x * Q->sin_delta_phi; + lp.lam = atan2(y * Q->cos_delta_gamma - z0 * Q->sin_delta_gamma, + x * Q->cos_delta_phi - z * Q->sin_delta_phi); + z0 = z0 * Q->cos_delta_gamma + y * Q->sin_delta_gamma; + lp.phi = asin(z0); + + lp.lam = adjlon(lp.lam); + + /* Adjust pre-projection */ + if (lp.lam + lp.phi < -fu) { + d = (lp.lam - lp.phi + 1.6) * (lp.lam + lp.phi + fu) / 8.; + lp.lam += d; + lp.phi -= 0.8 * d * sin(lp.phi + M_PI / 2.); + } + + /* Project with Hammer (1.68,2) */ + cosphi = cos(lp.phi); + d = sqrt(2./(1. + cosphi * cos(lp.lam / 2.))); + xy.x = w * d * cosphi * sin(lp.lam / 2.); + xy.y = d * sin(lp.phi); + + /* Adjust post-projection */ + d = (1. - cos(lp.lam * lp.phi)) / k; + if (xy.y < 0.) { + xy.x *= 1. + d; + } + if (xy.y > 0.) { + xy.x *= 1. + d / 1.5 * xy.x * xy.x; + } + + return xy; +} + + +PJ *PROJECTION(bertin1953) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + P->lam0 = 0; + P->phi0 = PJ_TORAD(-42.); + + Q->cos_delta_phi = cos(P->phi0); + Q->sin_delta_phi = sin(P->phi0); + Q->cos_delta_gamma = 1.; + Q->sin_delta_gamma = 0.; + + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_bipc.c b/src/PJ_bipc.c deleted file mode 100644 index e4a69077..00000000 --- a/src/PJ_bipc.c +++ /dev/null @@ -1,174 +0,0 @@ -#define PJ_LIB__ -#include -#include - -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(bipc, "Bipolar conic of western hemisphere") "\n\tConic Sph"; - -# define EPS 1e-10 -# define EPS10 1e-10 -# define ONEEPS 1.000000001 -# define NITER 10 -# define lamB -.34894976726250681539 -# define n .63055844881274687180 -# define F 1.89724742567461030582 -# define Azab .81650043674686363166 -# define Azba 1.82261843856185925133 -# define T 1.27246578267089012270 -# define rhoc 1.20709121521568721927 -# define cAzc .69691523038678375519 -# define sAzc .71715351331143607555 -# define C45 .70710678118654752469 -# define S45 .70710678118654752410 -# define C20 .93969262078590838411 -# define S20 -.34202014332566873287 -# define R110 1.91986217719376253360 -# define R104 1.81514242207410275904 - - -struct pj_opaque { - int noskew; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r; - int tag; - - cphi = cos(lp.phi); - sphi = sin(lp.phi); - cdlam = cos(sdlam = lamB - lp.lam); - sdlam = sin(sdlam); - if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) { - Az = lp.phi < 0. ? M_PI : 0.; - tphi = HUGE_VAL; - } else { - tphi = sphi / cphi; - Az = atan2(sdlam , C45 * (tphi - cdlam)); - } - if( (tag = (Az > Azba)) ) { - cdlam = cos(sdlam = lp.lam + R110); - sdlam = sin(sdlam); - z = S20 * sphi + C20 * cphi * cdlam; - if (fabs(z) > 1.) { - if (fabs(z) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - else z = z < 0. ? -1. : 1.; - } else - z = acos(z); - if (tphi != HUGE_VAL) - Az = atan2(sdlam, (C20 * tphi - S20 * cdlam)); - Av = Azab; - xy.y = rhoc; - } else { - z = S45 * (sphi + cphi * cdlam); - if (fabs(z) > 1.) { - if (fabs(z) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - else z = z < 0. ? -1. : 1.; - } else - z = acos(z); - Av = Azba; - xy.y = -rhoc; - } - if (z < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - r = F * (t = pow(tan(.5 * z), n)); - if ((al = .5 * (R104 - z)) < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - al = (t + pow(al, n)) / T; - if (fabs(al) > 1.) { - if (fabs(al) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - else al = al < 0. ? -1. : 1.; - } else - al = acos(al); - if (fabs(t = n * (Av - Az)) < al) - r /= cos(al + (tag ? t : -t)); - xy.x = r * sin(t); - xy.y += (tag ? -r : r) * cos(t); - if (Q->noskew) { - t = xy.x; - xy.x = -xy.x * cAzc - xy.y * sAzc; - xy.y = -xy.y * cAzc + t * sAzc; - } - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double t, r, rp, rl, al, z = 0.0, fAz, Az, s, c, Av; - int neg, i; - - if (Q->noskew) { - t = xy.x; - xy.x = -xy.x * cAzc + xy.y * sAzc; - xy.y = -xy.y * cAzc - t * sAzc; - } - if( (neg = (xy.x < 0.)) ) { - xy.y = rhoc - xy.y; - s = S20; - c = C20; - Av = Azab; - } else { - xy.y += rhoc; - s = S45; - c = C45; - Av = Azba; - } - rl = rp = r = hypot(xy.x, xy.y); - fAz = fabs(Az = atan2(xy.x, xy.y)); - for (i = NITER; i ; --i) { - z = 2. * atan(pow(r / F,1 / n)); - al = acos((pow(tan(.5 * z), n) + - pow(tan(.5 * (R104 - z)), n)) / T); - if (fAz < al) - r = rp * cos(al + (neg ? Az : -Az)); - if (fabs(rl - r) < EPS) - break; - rl = r; - } - if (! i) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - Az = Av - Az / n; - lp.phi = asin(s * cos(z) + c * sin(z) * cos(Az)); - lp.lam = atan2(sin(Az), c / tan(z) - s * cos(Az)); - if (neg) - lp.lam -= R110; - else - lp.lam = lamB - lp.lam; - return (lp); -} - - -PJ *PROJECTION(bipc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->noskew = pj_param(P->ctx, P->params, "bns").i; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - return P; -} diff --git a/src/PJ_bipc.cpp b/src/PJ_bipc.cpp new file mode 100644 index 00000000..243ecccd --- /dev/null +++ b/src/PJ_bipc.cpp @@ -0,0 +1,174 @@ +#define PJ_LIB__ +#include +#include + +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(bipc, "Bipolar conic of western hemisphere") "\n\tConic Sph"; + +# define EPS 1e-10 +# define EPS10 1e-10 +# define ONEEPS 1.000000001 +# define NITER 10 +# define lamB -.34894976726250681539 +# define n .63055844881274687180 +# define F 1.89724742567461030582 +# define Azab .81650043674686363166 +# define Azba 1.82261843856185925133 +# define T 1.27246578267089012270 +# define rhoc 1.20709121521568721927 +# define cAzc .69691523038678375519 +# define sAzc .71715351331143607555 +# define C45 .70710678118654752469 +# define S45 .70710678118654752410 +# define C20 .93969262078590838411 +# define S20 -.34202014332566873287 +# define R110 1.91986217719376253360 +# define R104 1.81514242207410275904 + + +struct pj_opaque { + int noskew; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r; + int tag; + + cphi = cos(lp.phi); + sphi = sin(lp.phi); + cdlam = cos(sdlam = lamB - lp.lam); + sdlam = sin(sdlam); + if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) { + Az = lp.phi < 0. ? M_PI : 0.; + tphi = HUGE_VAL; + } else { + tphi = sphi / cphi; + Az = atan2(sdlam , C45 * (tphi - cdlam)); + } + if( (tag = (Az > Azba)) ) { + cdlam = cos(sdlam = lp.lam + R110); + sdlam = sin(sdlam); + z = S20 * sphi + C20 * cphi * cdlam; + if (fabs(z) > 1.) { + if (fabs(z) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + else z = z < 0. ? -1. : 1.; + } else + z = acos(z); + if (tphi != HUGE_VAL) + Az = atan2(sdlam, (C20 * tphi - S20 * cdlam)); + Av = Azab; + xy.y = rhoc; + } else { + z = S45 * (sphi + cphi * cdlam); + if (fabs(z) > 1.) { + if (fabs(z) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + else z = z < 0. ? -1. : 1.; + } else + z = acos(z); + Av = Azba; + xy.y = -rhoc; + } + if (z < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + r = F * (t = pow(tan(.5 * z), n)); + if ((al = .5 * (R104 - z)) < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + al = (t + pow(al, n)) / T; + if (fabs(al) > 1.) { + if (fabs(al) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + else al = al < 0. ? -1. : 1.; + } else + al = acos(al); + if (fabs(t = n * (Av - Az)) < al) + r /= cos(al + (tag ? t : -t)); + xy.x = r * sin(t); + xy.y += (tag ? -r : r) * cos(t); + if (Q->noskew) { + t = xy.x; + xy.x = -xy.x * cAzc - xy.y * sAzc; + xy.y = -xy.y * cAzc + t * sAzc; + } + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t, r, rp, rl, al, z = 0.0, fAz, Az, s, c, Av; + int neg, i; + + if (Q->noskew) { + t = xy.x; + xy.x = -xy.x * cAzc + xy.y * sAzc; + xy.y = -xy.y * cAzc - t * sAzc; + } + if( (neg = (xy.x < 0.)) ) { + xy.y = rhoc - xy.y; + s = S20; + c = C20; + Av = Azab; + } else { + xy.y += rhoc; + s = S45; + c = C45; + Av = Azba; + } + rl = rp = r = hypot(xy.x, xy.y); + fAz = fabs(Az = atan2(xy.x, xy.y)); + for (i = NITER; i ; --i) { + z = 2. * atan(pow(r / F,1 / n)); + al = acos((pow(tan(.5 * z), n) + + pow(tan(.5 * (R104 - z)), n)) / T); + if (fAz < al) + r = rp * cos(al + (neg ? Az : -Az)); + if (fabs(rl - r) < EPS) + break; + rl = r; + } + if (! i) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + Az = Av - Az / n; + lp.phi = asin(s * cos(z) + c * sin(z) * cos(Az)); + lp.lam = atan2(sin(Az), c / tan(z) - s * cos(Az)); + if (neg) + lp.lam -= R110; + else + lp.lam = lamB - lp.lam; + return (lp); +} + + +PJ *PROJECTION(bipc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->noskew = pj_param(P->ctx, P->params, "bns").i; + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} diff --git a/src/PJ_boggs.c b/src/PJ_boggs.c deleted file mode 100644 index 119357c0..00000000 --- a/src/PJ_boggs.c +++ /dev/null @@ -1,43 +0,0 @@ -#define PJ_LIB__ -#include - -#include "projects.h" - -PROJ_HEAD(boggs, "Boggs Eumorphic") "\n\tPCyl, no inv, Sph"; -# define NITER 20 -# define EPS 1e-7 -# define FXC 2.00276 -# define FXC2 1.11072 -# define FYC 0.49931 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double theta, th1, c; - int i; - (void) P; - - theta = lp.phi; - if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) - xy.x = 0.; - else { - c = sin(theta) * M_PI; - for (i = NITER; i; --i) { - theta -= th1 = (theta + sin(theta) - c) / - (1. + cos(theta)); - if (fabs(th1) < EPS) break; - } - theta *= 0.5; - xy.x = FXC * lp.lam / (1. / cos(lp.phi) + FXC2 / cos(theta)); - } - xy.y = FYC * (lp.phi + M_SQRT2 * sin(theta)); - return (xy); -} - - - -PJ *PROJECTION(boggs) { - P->es = 0.; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_boggs.cpp b/src/PJ_boggs.cpp new file mode 100644 index 00000000..119357c0 --- /dev/null +++ b/src/PJ_boggs.cpp @@ -0,0 +1,43 @@ +#define PJ_LIB__ +#include + +#include "projects.h" + +PROJ_HEAD(boggs, "Boggs Eumorphic") "\n\tPCyl, no inv, Sph"; +# define NITER 20 +# define EPS 1e-7 +# define FXC 2.00276 +# define FXC2 1.11072 +# define FYC 0.49931 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double theta, th1, c; + int i; + (void) P; + + theta = lp.phi; + if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) + xy.x = 0.; + else { + c = sin(theta) * M_PI; + for (i = NITER; i; --i) { + theta -= th1 = (theta + sin(theta) - c) / + (1. + cos(theta)); + if (fabs(th1) < EPS) break; + } + theta *= 0.5; + xy.x = FXC * lp.lam / (1. / cos(lp.phi) + FXC2 / cos(theta)); + } + xy.y = FYC * (lp.phi + M_SQRT2 * sin(theta)); + return (xy); +} + + + +PJ *PROJECTION(boggs) { + P->es = 0.; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_bonne.c b/src/PJ_bonne.c deleted file mode 100644 index d3d5b757..00000000 --- a/src/PJ_bonne.c +++ /dev/null @@ -1,134 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - - -PROJ_HEAD(bonne, "Bonne (Werner lat_1=90)") - "\n\tConic Sph&Ell\n\tlat_1="; -#define EPS10 1e-10 - -struct pj_opaque { - double phi1; - double cphi1; - double am1; - double m1; - double *en; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double rh, E, c; - - rh = Q->am1 + Q->m1 - pj_mlfn(lp.phi, E = sin(lp.phi), c = cos(lp.phi), Q->en); - E = c * lp.lam / (rh * sqrt(1. - P->es * E * E)); - xy.x = rh * sin(E); - xy.y = Q->am1 - rh * cos(E); - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double E, rh; - - rh = Q->cphi1 + Q->phi1 - lp.phi; - if (fabs(rh) > EPS10) { - xy.x = rh * sin(E = lp.lam * cos(lp.phi) / rh); - xy.y = Q->cphi1 - rh * cos(E); - } else - xy.x = xy.y = 0.; - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double rh; - - rh = hypot(xy.x, xy.y = Q->cphi1 - xy.y); - lp.phi = Q->cphi1 + Q->phi1 - rh; - if (fabs(lp.phi) > M_HALFPI) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) - lp.lam = 0.; - else - lp.lam = rh * atan2(xy.x, xy.y) / cos(lp.phi); - return lp; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double s, rh; - - rh = hypot(xy.x, xy.y = Q->am1 - xy.y); - lp.phi = pj_inv_mlfn(P->ctx, Q->am1 + Q->m1 - rh, P->es, Q->en); - if ((s = fabs(lp.phi)) < M_HALFPI) { - s = sin(lp.phi); - lp.lam = rh * atan2(xy.x, xy.y) * - sqrt(1. - P->es * s * s) / cos(lp.phi); - } else if (fabs(s - M_HALFPI) <= EPS10) - lp.lam = 0.; - else { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - return lp; -} - - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(bonne) { - double c; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (fabs(Q->phi1) < EPS10) - return destructor (P, PJD_ERR_LAT1_IS_ZERO); - - if (P->es != 0.0) { - Q->en = pj_enfn(P->es); - if (0==Q->en) - return destructor(P, ENOMEM); - Q->m1 = pj_mlfn(Q->phi1, Q->am1 = sin(Q->phi1), - c = cos(Q->phi1), Q->en); - Q->am1 = c / (sqrt(1. - P->es * Q->am1 * Q->am1) * Q->am1); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - if (fabs(Q->phi1) + EPS10 >= M_HALFPI) - Q->cphi1 = 0.; - else - Q->cphi1 = 1. / tan(Q->phi1); - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; -} - - diff --git a/src/PJ_bonne.cpp b/src/PJ_bonne.cpp new file mode 100644 index 00000000..6c51af7c --- /dev/null +++ b/src/PJ_bonne.cpp @@ -0,0 +1,134 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + + +PROJ_HEAD(bonne, "Bonne (Werner lat_1=90)") + "\n\tConic Sph&Ell\n\tlat_1="; +#define EPS10 1e-10 + +struct pj_opaque { + double phi1; + double cphi1; + double am1; + double m1; + double *en; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rh, E, c; + + rh = Q->am1 + Q->m1 - pj_mlfn(lp.phi, E = sin(lp.phi), c = cos(lp.phi), Q->en); + E = c * lp.lam / (rh * sqrt(1. - P->es * E * E)); + xy.x = rh * sin(E); + xy.y = Q->am1 - rh * cos(E); + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double E, rh; + + rh = Q->cphi1 + Q->phi1 - lp.phi; + if (fabs(rh) > EPS10) { + xy.x = rh * sin(E = lp.lam * cos(lp.phi) / rh); + xy.y = Q->cphi1 - rh * cos(E); + } else + xy.x = xy.y = 0.; + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rh; + + rh = hypot(xy.x, xy.y = Q->cphi1 - xy.y); + lp.phi = Q->cphi1 + Q->phi1 - rh; + if (fabs(lp.phi) > M_HALFPI) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) + lp.lam = 0.; + else + lp.lam = rh * atan2(xy.x, xy.y) / cos(lp.phi); + return lp; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double s, rh; + + rh = hypot(xy.x, xy.y = Q->am1 - xy.y); + lp.phi = pj_inv_mlfn(P->ctx, Q->am1 + Q->m1 - rh, P->es, Q->en); + if ((s = fabs(lp.phi)) < M_HALFPI) { + s = sin(lp.phi); + lp.lam = rh * atan2(xy.x, xy.y) * + sqrt(1. - P->es * s * s) / cos(lp.phi); + } else if (fabs(s - M_HALFPI) <= EPS10) + lp.lam = 0.; + else { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + return lp; +} + + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(bonne) { + double c; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (fabs(Q->phi1) < EPS10) + return destructor (P, PJD_ERR_LAT1_IS_ZERO); + + if (P->es != 0.0) { + Q->en = pj_enfn(P->es); + if (0==Q->en) + return destructor(P, ENOMEM); + Q->m1 = pj_mlfn(Q->phi1, Q->am1 = sin(Q->phi1), + c = cos(Q->phi1), Q->en); + Q->am1 = c / (sqrt(1. - P->es * Q->am1 * Q->am1) * Q->am1); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + if (fabs(Q->phi1) + EPS10 >= M_HALFPI) + Q->cphi1 = 0.; + else + Q->cphi1 = 1. / tan(Q->phi1); + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; +} + + diff --git a/src/PJ_calcofi.c b/src/PJ_calcofi.c deleted file mode 100644 index ed4cfe86..00000000 --- a/src/PJ_calcofi.c +++ /dev/null @@ -1,163 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" -#include "proj_api.h" - -PROJ_HEAD(calcofi, - "Cal Coop Ocean Fish Invest Lines/Stations") "\n\tCyl, Sph&Ell"; - - -/* Conversions for the California Cooperative Oceanic Fisheries Investigations -Line/Station coordinate system following the algorithm of: -Eber, L.E., and R.P. Hewitt. 1979. Conversion algorithms for the CalCOFI -station grid. California Cooperative Oceanic Fisheries Investigations Reports -20:135-137. (corrected for typographical errors). -http://www.calcofi.org/publications/calcofireports/v20/Vol_20_Eber___Hewitt.pdf -They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or -meridional units at reference point O, and similarly 1 unit of CalCOFI -Station == 1/15 of a degree at O. -By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use -whatever ellipsoid is provided. */ - - -#define EPS10 1.e-10 -#define DEG_TO_LINE 5 -#define DEG_TO_STATION 15 -#define LINE_TO_RAD 0.0034906585039886592 -#define STATION_TO_RAD 0.0011635528346628863 -#define PT_O_LINE 80 /* reference point O is at line 80, */ -#define PT_O_STATION 60 /* station 60, */ -#define PT_O_LAMBDA -2.1144663887911301 /* lon -121.15 and */ -#define PT_O_PHI 0.59602993955606354 /* lat 34.15 */ -#define ROTATION_ANGLE 0.52359877559829882 /*CalCOFI angle of 30 deg in rad */ - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - double oy; /* pt O y value in Mercator */ - double l1; /* l1 and l2 are distances calculated using trig that sum - to the east/west distance between point O and point xy */ - double l2; - double ry; /* r is the point on the same station as o (60) and the same - line as xy xy, r, o form a right triangle */ - - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - xy.x = lp.lam; - xy.y = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); /* Mercator transform xy*/ - oy = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e)); - l1 = (xy.y - oy) * tan(ROTATION_ANGLE); - l2 = -xy.x - l1 + PT_O_LAMBDA; - ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; - ry = pj_phi2(P->ctx, exp(-ry), P->e); /*inverse Mercator*/ - xy.x = PT_O_LINE - RAD_TO_DEG * - (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); - xy.y = PT_O_STATION + RAD_TO_DEG * - (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); - /* set a = 1, x0 = 0, and y0 = 0 so that no further unit adjustments - are done */ - - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double oy; - double l1; - double l2; - double ry; - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = lp.lam; - xy.y = log(tan(M_FORTPI + .5 * lp.phi)); - oy = log(tan(M_FORTPI + .5 * PT_O_PHI)); - l1 = (xy.y - oy) * tan(ROTATION_ANGLE); - l2 = -xy.x - l1 + PT_O_LAMBDA; - ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; - ry = M_HALFPI - 2. * atan(exp(-ry)); - xy.x = PT_O_LINE - RAD_TO_DEG * - (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); - xy.y = PT_O_STATION + RAD_TO_DEG * - (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - double ry; /* y value of point r */ - double oymctr; /* Mercator-transformed y value of point O */ - double rymctr; /* Mercator-transformed ry */ - double xymctr; /* Mercator-transformed xy.y */ - double l1; - double l2; - - ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * - cos(ROTATION_ANGLE); - lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); - oymctr = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e)); - rymctr = -log(pj_tsfn(ry, sin(ry), P->e)); - xymctr = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); - l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE); - l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); - lp.lam = PT_O_LAMBDA - (l1 + l2); - - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double ry; - double oymctr; - double rymctr; - double xymctr; - double l1; - double l2; - (void) P; - - ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * - cos(ROTATION_ANGLE); - lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); - oymctr = log(tan(M_FORTPI + .5 * PT_O_PHI)); - rymctr = log(tan(M_FORTPI + .5 * ry)); - xymctr = log(tan(M_FORTPI + .5 * lp.phi)); - l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE); - l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); - lp.lam = PT_O_LAMBDA - (l1 + l2); - - return lp; -} - - -PJ *PROJECTION(calcofi) { - P->opaque = 0; - - /* if the user has specified +lon_0 or +k0 for some reason, - we're going to ignore it so that xy is consistent with point O */ - P->lam0 = 0; - P->ra = 1; - P->a = 1; - P->x0 = 0; - P->y0 = 0; - P->over = 1; - - if (P->es != 0.0) { /* ellipsoid */ - P->inv = e_inverse; - P->fwd = e_forward; - } else { /* sphere */ - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; -} diff --git a/src/PJ_calcofi.cpp b/src/PJ_calcofi.cpp new file mode 100644 index 00000000..ed4cfe86 --- /dev/null +++ b/src/PJ_calcofi.cpp @@ -0,0 +1,163 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" +#include "proj_api.h" + +PROJ_HEAD(calcofi, + "Cal Coop Ocean Fish Invest Lines/Stations") "\n\tCyl, Sph&Ell"; + + +/* Conversions for the California Cooperative Oceanic Fisheries Investigations +Line/Station coordinate system following the algorithm of: +Eber, L.E., and R.P. Hewitt. 1979. Conversion algorithms for the CalCOFI +station grid. California Cooperative Oceanic Fisheries Investigations Reports +20:135-137. (corrected for typographical errors). +http://www.calcofi.org/publications/calcofireports/v20/Vol_20_Eber___Hewitt.pdf +They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or +meridional units at reference point O, and similarly 1 unit of CalCOFI +Station == 1/15 of a degree at O. +By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use +whatever ellipsoid is provided. */ + + +#define EPS10 1.e-10 +#define DEG_TO_LINE 5 +#define DEG_TO_STATION 15 +#define LINE_TO_RAD 0.0034906585039886592 +#define STATION_TO_RAD 0.0011635528346628863 +#define PT_O_LINE 80 /* reference point O is at line 80, */ +#define PT_O_STATION 60 /* station 60, */ +#define PT_O_LAMBDA -2.1144663887911301 /* lon -121.15 and */ +#define PT_O_PHI 0.59602993955606354 /* lat 34.15 */ +#define ROTATION_ANGLE 0.52359877559829882 /*CalCOFI angle of 30 deg in rad */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + double oy; /* pt O y value in Mercator */ + double l1; /* l1 and l2 are distances calculated using trig that sum + to the east/west distance between point O and point xy */ + double l2; + double ry; /* r is the point on the same station as o (60) and the same + line as xy xy, r, o form a right triangle */ + + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + xy.x = lp.lam; + xy.y = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); /* Mercator transform xy*/ + oy = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e)); + l1 = (xy.y - oy) * tan(ROTATION_ANGLE); + l2 = -xy.x - l1 + PT_O_LAMBDA; + ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; + ry = pj_phi2(P->ctx, exp(-ry), P->e); /*inverse Mercator*/ + xy.x = PT_O_LINE - RAD_TO_DEG * + (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); + xy.y = PT_O_STATION + RAD_TO_DEG * + (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); + /* set a = 1, x0 = 0, and y0 = 0 so that no further unit adjustments + are done */ + + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double oy; + double l1; + double l2; + double ry; + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = lp.lam; + xy.y = log(tan(M_FORTPI + .5 * lp.phi)); + oy = log(tan(M_FORTPI + .5 * PT_O_PHI)); + l1 = (xy.y - oy) * tan(ROTATION_ANGLE); + l2 = -xy.x - l1 + PT_O_LAMBDA; + ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; + ry = M_HALFPI - 2. * atan(exp(-ry)); + xy.x = PT_O_LINE - RAD_TO_DEG * + (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); + xy.y = PT_O_STATION + RAD_TO_DEG * + (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + double ry; /* y value of point r */ + double oymctr; /* Mercator-transformed y value of point O */ + double rymctr; /* Mercator-transformed ry */ + double xymctr; /* Mercator-transformed xy.y */ + double l1; + double l2; + + ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * + cos(ROTATION_ANGLE); + lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); + oymctr = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e)); + rymctr = -log(pj_tsfn(ry, sin(ry), P->e)); + xymctr = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); + l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE); + l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); + lp.lam = PT_O_LAMBDA - (l1 + l2); + + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double ry; + double oymctr; + double rymctr; + double xymctr; + double l1; + double l2; + (void) P; + + ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * + cos(ROTATION_ANGLE); + lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); + oymctr = log(tan(M_FORTPI + .5 * PT_O_PHI)); + rymctr = log(tan(M_FORTPI + .5 * ry)); + xymctr = log(tan(M_FORTPI + .5 * lp.phi)); + l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE); + l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); + lp.lam = PT_O_LAMBDA - (l1 + l2); + + return lp; +} + + +PJ *PROJECTION(calcofi) { + P->opaque = 0; + + /* if the user has specified +lon_0 or +k0 for some reason, + we're going to ignore it so that xy is consistent with point O */ + P->lam0 = 0; + P->ra = 1; + P->a = 1; + P->x0 = 0; + P->y0 = 0; + P->over = 1; + + if (P->es != 0.0) { /* ellipsoid */ + P->inv = e_inverse; + P->fwd = e_forward; + } else { /* sphere */ + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; +} diff --git a/src/PJ_cart.c b/src/PJ_cart.c deleted file mode 100644 index 6fed9985..00000000 --- a/src/PJ_cart.c +++ /dev/null @@ -1,219 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Convert between ellipsoidal, geodetic coordinates and - * cartesian, geocentric coordinates. - * - * Formally, this functionality is also found in the PJ_geocent.c - * code. - * - * Actually, however, the PJ_geocent transformations are carried - * out in concert between 2D stubs in PJ_geocent.c and 3D code - * placed in pj_transform.c. - * - * For pipeline-style datum shifts, we do need direct access - * to the full 3D interface for this functionality. - * - * Hence this code, which may look like "just another PJ_geocent" - * but really is something substantially different. - * - * Author: Thomas Knudsen, thokn@sdfe.dk - * - ****************************************************************************** - * Copyright (c) 2016, Thomas Knudsen / SDFE - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include "proj_internal.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(cart, "Geodetic/cartesian conversions"); - - -/************************************************************** - CARTESIAN / GEODETIC CONVERSIONS -*************************************************************** - This material follows: - - Bernhard Hofmann-Wellenhof & Helmut Moritz: - Physical Geodesy, 2nd edition. - Springer, 2005. - - chapter 5.6: Coordinate transformations - (HM, below), - - and - - Wikipedia: Geographic Coordinate Conversion, - https://en.wikipedia.org/wiki/Geographic_coordinate_conversion - - (WP, below). - - The cartesian-to-geodetic conversion is based on Bowring's - celebrated method: - - B. R. Bowring: - Transformation from spatial to geographical coordinates - Survey Review 23(181), pp. 323-327, 1976 - - (BB, below), - - but could probably use some TLC from a newer and faster - algorithm: - - Toshio Fukushima: - Transformation from Cartesian to Geodetic Coordinates - Accelerated by Halley’s Method - Journal of Geodesy, February 2006 - - (TF, below). - - Close to the poles, we avoid singularities by switching to an - approximation requiring knowledge of the geocentric radius - at the given latitude. For this, we use an adaptation of the - formula given in: - - Wikipedia: Earth Radius - https://en.wikipedia.org/wiki/Earth_radius#Radius_at_a_given_geodetic_latitude - (Derivation and commentary at https://gis.stackexchange.com/q/20200) - - (WP2, below) - - These routines are probably not as robust at those in - geocent.c, at least thay haven't been through as heavy - use as their geocent sisters. Some care has been taken - to avoid singularities, but extreme cases (e.g. setting - es, the squared eccentricity, to 1), will cause havoc. - -**************************************************************/ - - -/*********************************************************************/ -static double normal_radius_of_curvature (double a, double es, double phi) { -/*********************************************************************/ - double s = sin(phi); - if (es==0) - return a; - /* This is from WP. HM formula 2-149 gives an a,b version */ - return a / sqrt (1 - es*s*s); -} - -/*********************************************************************/ -static double geocentric_radius (double a, double b, double phi) { -/********************************************************************* - Return the geocentric radius at latitude phi, of an ellipsoid - with semimajor axis a and semiminor axis b. - - This is from WP2, but uses hypot() for potentially better - numerical robustness -***********************************************************************/ - return hypot (a*a*cos (phi), b*b*sin(phi)) / hypot (a*cos(phi), b*sin(phi)); -} - - -/*********************************************************************/ -static XYZ cartesian (LPZ geod, PJ *P) { -/*********************************************************************/ - double N, cosphi = cos(geod.phi); - XYZ xyz; - - N = normal_radius_of_curvature(P->a, P->es, geod.phi); - - /* HM formula 5-27 (z formula follows WP) */ - xyz.x = (N + geod.z) * cosphi * cos(geod.lam); - xyz.y = (N + geod.z) * cosphi * sin(geod.lam); - xyz.z = (N * (1 - P->es) + geod.z) * sin(geod.phi); - - return xyz; -} - - -/*********************************************************************/ -static LPZ geodetic (XYZ cart, PJ *P) { -/*********************************************************************/ - double N, p, theta, c, s; - LPZ lpz; - - /* Perpendicular distance from point to Z-axis (HM eq. 5-28) */ - p = hypot (cart.x, cart.y); - - /* HM eq. (5-37) */ - theta = atan2 (cart.z * P->a, p * P->b); - - /* HM eq. (5-36) (from BB, 1976) */ - c = cos(theta); - s = sin(theta); - lpz.phi = atan2 (cart.z + P->e2s*P->b*s*s*s, p - P->es*P->a*c*c*c); - lpz.lam = atan2 (cart.y, cart.x); - N = normal_radius_of_curvature (P->a, P->es, lpz.phi); - - - c = cos(lpz.phi); - if (fabs(c) < 1e-6) { - /* poleward of 89.99994 deg, we avoid division by zero */ - /* by computing the height as the cartesian z value */ - /* minus the geocentric radius of the Earth at the given */ - /* latitude */ - double r = geocentric_radius (P->a, P->b, lpz.phi); - lpz.z = fabs (cart.z) - r; - } - else - lpz.z = p / c - N; - - return lpz; -} - - - -/* In effect, 2 cartesian coordinates of a point on the ellipsoid. Rather pointless, but... */ -static XY cart_forward (LP lp, PJ *P) { - PJ_COORD point; - point.lp = lp; - point.lpz.z = 0; - - point.xyz = cartesian (point.lpz, P); - return point.xy; -} - -/* And the other way round. Still rather pointless, but... */ -static LP cart_reverse (XY xy, PJ *P) { - PJ_COORD point; - point.xy = xy; - point.xyz.z = 0; - - point.lpz = geodetic (point.xyz, P); - return point.lp; -} - - - -/*********************************************************************/ -PJ *CONVERSION(cart,1) { -/*********************************************************************/ - P->fwd3d = cartesian; - P->inv3d = geodetic; - P->fwd = cart_forward; - P->inv = cart_reverse; - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_CARTESIAN; - return P; -} diff --git a/src/PJ_cart.cpp b/src/PJ_cart.cpp new file mode 100644 index 00000000..6fed9985 --- /dev/null +++ b/src/PJ_cart.cpp @@ -0,0 +1,219 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Convert between ellipsoidal, geodetic coordinates and + * cartesian, geocentric coordinates. + * + * Formally, this functionality is also found in the PJ_geocent.c + * code. + * + * Actually, however, the PJ_geocent transformations are carried + * out in concert between 2D stubs in PJ_geocent.c and 3D code + * placed in pj_transform.c. + * + * For pipeline-style datum shifts, we do need direct access + * to the full 3D interface for this functionality. + * + * Hence this code, which may look like "just another PJ_geocent" + * but really is something substantially different. + * + * Author: Thomas Knudsen, thokn@sdfe.dk + * + ****************************************************************************** + * Copyright (c) 2016, Thomas Knudsen / SDFE + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include "proj_internal.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(cart, "Geodetic/cartesian conversions"); + + +/************************************************************** + CARTESIAN / GEODETIC CONVERSIONS +*************************************************************** + This material follows: + + Bernhard Hofmann-Wellenhof & Helmut Moritz: + Physical Geodesy, 2nd edition. + Springer, 2005. + + chapter 5.6: Coordinate transformations + (HM, below), + + and + + Wikipedia: Geographic Coordinate Conversion, + https://en.wikipedia.org/wiki/Geographic_coordinate_conversion + + (WP, below). + + The cartesian-to-geodetic conversion is based on Bowring's + celebrated method: + + B. R. Bowring: + Transformation from spatial to geographical coordinates + Survey Review 23(181), pp. 323-327, 1976 + + (BB, below), + + but could probably use some TLC from a newer and faster + algorithm: + + Toshio Fukushima: + Transformation from Cartesian to Geodetic Coordinates + Accelerated by Halley’s Method + Journal of Geodesy, February 2006 + + (TF, below). + + Close to the poles, we avoid singularities by switching to an + approximation requiring knowledge of the geocentric radius + at the given latitude. For this, we use an adaptation of the + formula given in: + + Wikipedia: Earth Radius + https://en.wikipedia.org/wiki/Earth_radius#Radius_at_a_given_geodetic_latitude + (Derivation and commentary at https://gis.stackexchange.com/q/20200) + + (WP2, below) + + These routines are probably not as robust at those in + geocent.c, at least thay haven't been through as heavy + use as their geocent sisters. Some care has been taken + to avoid singularities, but extreme cases (e.g. setting + es, the squared eccentricity, to 1), will cause havoc. + +**************************************************************/ + + +/*********************************************************************/ +static double normal_radius_of_curvature (double a, double es, double phi) { +/*********************************************************************/ + double s = sin(phi); + if (es==0) + return a; + /* This is from WP. HM formula 2-149 gives an a,b version */ + return a / sqrt (1 - es*s*s); +} + +/*********************************************************************/ +static double geocentric_radius (double a, double b, double phi) { +/********************************************************************* + Return the geocentric radius at latitude phi, of an ellipsoid + with semimajor axis a and semiminor axis b. + + This is from WP2, but uses hypot() for potentially better + numerical robustness +***********************************************************************/ + return hypot (a*a*cos (phi), b*b*sin(phi)) / hypot (a*cos(phi), b*sin(phi)); +} + + +/*********************************************************************/ +static XYZ cartesian (LPZ geod, PJ *P) { +/*********************************************************************/ + double N, cosphi = cos(geod.phi); + XYZ xyz; + + N = normal_radius_of_curvature(P->a, P->es, geod.phi); + + /* HM formula 5-27 (z formula follows WP) */ + xyz.x = (N + geod.z) * cosphi * cos(geod.lam); + xyz.y = (N + geod.z) * cosphi * sin(geod.lam); + xyz.z = (N * (1 - P->es) + geod.z) * sin(geod.phi); + + return xyz; +} + + +/*********************************************************************/ +static LPZ geodetic (XYZ cart, PJ *P) { +/*********************************************************************/ + double N, p, theta, c, s; + LPZ lpz; + + /* Perpendicular distance from point to Z-axis (HM eq. 5-28) */ + p = hypot (cart.x, cart.y); + + /* HM eq. (5-37) */ + theta = atan2 (cart.z * P->a, p * P->b); + + /* HM eq. (5-36) (from BB, 1976) */ + c = cos(theta); + s = sin(theta); + lpz.phi = atan2 (cart.z + P->e2s*P->b*s*s*s, p - P->es*P->a*c*c*c); + lpz.lam = atan2 (cart.y, cart.x); + N = normal_radius_of_curvature (P->a, P->es, lpz.phi); + + + c = cos(lpz.phi); + if (fabs(c) < 1e-6) { + /* poleward of 89.99994 deg, we avoid division by zero */ + /* by computing the height as the cartesian z value */ + /* minus the geocentric radius of the Earth at the given */ + /* latitude */ + double r = geocentric_radius (P->a, P->b, lpz.phi); + lpz.z = fabs (cart.z) - r; + } + else + lpz.z = p / c - N; + + return lpz; +} + + + +/* In effect, 2 cartesian coordinates of a point on the ellipsoid. Rather pointless, but... */ +static XY cart_forward (LP lp, PJ *P) { + PJ_COORD point; + point.lp = lp; + point.lpz.z = 0; + + point.xyz = cartesian (point.lpz, P); + return point.xy; +} + +/* And the other way round. Still rather pointless, but... */ +static LP cart_reverse (XY xy, PJ *P) { + PJ_COORD point; + point.xy = xy; + point.xyz.z = 0; + + point.lpz = geodetic (point.xyz, P); + return point.lp; +} + + + +/*********************************************************************/ +PJ *CONVERSION(cart,1) { +/*********************************************************************/ + P->fwd3d = cartesian; + P->inv3d = geodetic; + P->fwd = cart_forward; + P->inv = cart_reverse; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_CARTESIAN; + return P; +} diff --git a/src/PJ_cass.c b/src/PJ_cass.c deleted file mode 100644 index 4e3b4251..00000000 --- a/src/PJ_cass.c +++ /dev/null @@ -1,121 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell"; - - -# define C1 .16666666666666666666 -# define C2 .00833333333333333333 -# define C3 .04166666666666666666 -# define C4 .33333333333333333333 -# define C5 .06666666666666666666 - - -struct pj_opaque { - double *en; - double m0; -}; - - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - double n, t, a1, c, a2, tn; - XY xy = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y = pj_mlfn (lp.phi, n = sin (lp.phi), c = cos (lp.phi), Q->en); - - n = 1./sqrt(1. - P->es * n*n); - tn = tan(lp.phi); t = tn * tn; - a1 = lp.lam * c; - c *= P->es * c / (1 - P->es); - a2 = a1 * a1; - - xy.x = n * a1 * (1. - a2 * t * - (C1 - (8. - t + 8. * c) * a2 * C2)); - xy.y -= Q->m0 - n * tn * a2 * - (.5 + (5. - t + 6. * c) * a2 * C3); - - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - xy.x = asin (cos (lp.phi) * sin (lp.lam)); - xy.y = atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0; - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - double n, t, r, dd, d2, tn, ph1; - LP lp = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - - ph1 = pj_inv_mlfn (P->ctx, Q->m0 + xy.y, P->es, Q->en); - tn = tan (ph1); t = tn*tn; - n = sin (ph1); - r = 1. / (1. - P->es * n * n); - n = sqrt (r); - r *= (1. - P->es) * n; - dd = xy.x / n; - d2 = dd * dd; - lp.phi = ph1 - (n * tn / r) * d2 * - (.5 - (1. + 3. * t) * d2 * C3); - lp.lam = dd * (1. + t * d2 * - (-C4 + (1. + 3. * t) * d2 * C5)) / cos (ph1); - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double dd; - lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x)); - lp.lam = atan2(tan(xy.x), cos(dd)); - return lp; -} - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - - -PJ *PROJECTION(cass) { - - /* Spheroidal? */ - if (0==P->es) { - P->inv = s_inverse; - P->fwd = s_forward; - return P; - } - - /* otherwise it's ellipsoidal */ - P->opaque = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==P->opaque) - return pj_default_destructor (P, ENOMEM); - P->destructor = destructor; - - P->opaque->en = pj_enfn (P->es); - if (0==P->opaque->en) - return pj_default_destructor (P, ENOMEM); - - P->opaque->m0 = pj_mlfn (P->phi0, sin (P->phi0), cos (P->phi0), P->opaque->en); - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_cass.cpp b/src/PJ_cass.cpp new file mode 100644 index 00000000..cc610940 --- /dev/null +++ b/src/PJ_cass.cpp @@ -0,0 +1,121 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell"; + + +# define C1 .16666666666666666666 +# define C2 .00833333333333333333 +# define C3 .04166666666666666666 +# define C4 .33333333333333333333 +# define C5 .06666666666666666666 + + +struct pj_opaque { + double *en; + double m0; +}; + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + double n, t, a1, c, a2, tn; + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y = pj_mlfn (lp.phi, n = sin (lp.phi), c = cos (lp.phi), Q->en); + + n = 1./sqrt(1. - P->es * n*n); + tn = tan(lp.phi); t = tn * tn; + a1 = lp.lam * c; + c *= P->es * c / (1 - P->es); + a2 = a1 * a1; + + xy.x = n * a1 * (1. - a2 * t * + (C1 - (8. - t + 8. * c) * a2 * C2)); + xy.y -= Q->m0 - n * tn * a2 * + (.5 + (5. - t + 6. * c) * a2 * C3); + + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + xy.x = asin (cos (lp.phi) * sin (lp.lam)); + xy.y = atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + double n, t, r, dd, d2, tn, ph1; + LP lp = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + ph1 = pj_inv_mlfn (P->ctx, Q->m0 + xy.y, P->es, Q->en); + tn = tan (ph1); t = tn*tn; + n = sin (ph1); + r = 1. / (1. - P->es * n * n); + n = sqrt (r); + r *= (1. - P->es) * n; + dd = xy.x / n; + d2 = dd * dd; + lp.phi = ph1 - (n * tn / r) * d2 * + (.5 - (1. + 3. * t) * d2 * C3); + lp.lam = dd * (1. + t * d2 * + (-C4 + (1. + 3. * t) * d2 * C5)) / cos (ph1); + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double dd; + lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x)); + lp.lam = atan2(tan(xy.x), cos(dd)); + return lp; +} + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + + +PJ *PROJECTION(cass) { + + /* Spheroidal? */ + if (0==P->es) { + P->inv = s_inverse; + P->fwd = s_forward; + return P; + } + + /* otherwise it's ellipsoidal */ + P->opaque = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==P->opaque) + return pj_default_destructor (P, ENOMEM); + P->destructor = destructor; + + static_cast(P->opaque)->en = pj_enfn (P->es); + if (0==static_cast(P->opaque)->en) + return pj_default_destructor (P, ENOMEM); + + static_cast(P->opaque)->m0 = pj_mlfn (P->phi0, sin (P->phi0), cos (P->phi0), static_cast(P->opaque)->en); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_cc.c b/src/PJ_cc.c deleted file mode 100644 index 152e6e4a..00000000 --- a/src/PJ_cc.c +++ /dev/null @@ -1,41 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(cc, "Central Cylindrical") "\n\tCyl, Sph"; -#define EPS10 1.e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - if (fabs (fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = lp.lam; - xy.y = tan(lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - lp.phi = atan(xy.y); - lp.lam = xy.x; - return lp; -} - - - -PJ *PROJECTION(cc) { - P->es = 0.; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_cc.cpp b/src/PJ_cc.cpp new file mode 100644 index 00000000..152e6e4a --- /dev/null +++ b/src/PJ_cc.cpp @@ -0,0 +1,41 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(cc, "Central Cylindrical") "\n\tCyl, Sph"; +#define EPS10 1.e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + if (fabs (fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = lp.lam; + xy.y = tan(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + lp.phi = atan(xy.y); + lp.lam = xy.x; + return lp; +} + + + +PJ *PROJECTION(cc) { + P->es = 0.; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_ccon.c b/src/PJ_ccon.c deleted file mode 100644 index 0b8b70cb..00000000 --- a/src/PJ_ccon.c +++ /dev/null @@ -1,107 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017, Lukasz Komsta - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -#define EPS10 1e-10 - -struct pj_opaque { - double phi1; - double ctgphi1; - double sinphi1; - double cosphi1; - double *en; -}; - -PROJ_HEAD(ccon, "Central Conic") - "\n\tCentral Conic, Sph\n\tlat_1="; - - - -static XY forward (LP lp, PJ *P) { - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double r; - - r = Q->ctgphi1 - tan(lp.phi - Q->phi1); - xy.x = r * sin(lp.lam * Q->sinphi1); - xy.y = Q->ctgphi1 - r * cos(lp.lam * Q->sinphi1); - - return xy; -} - - -static LP inverse (XY xy, PJ *P) { - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y = Q->ctgphi1 - xy.y; - lp.phi = Q->phi1 - atan(hypot(xy.x,xy.y) - Q->ctgphi1); - lp.lam = atan2(xy.x,xy.y)/Q->sinphi1; - - return lp; -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(ccon) { - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (fabs(Q->phi1) < EPS10) - return destructor (P, PJD_ERR_LAT1_IS_ZERO); - - if (!(Q->en = pj_enfn(P->es))) - return destructor(P, ENOMEM); - - Q->sinphi1 = sin(Q->phi1); - Q->cosphi1 = cos(Q->phi1); - Q->ctgphi1 = Q->cosphi1/Q->sinphi1; - - - P->inv = inverse; - P->fwd = forward; - - return P; -} - - diff --git a/src/PJ_ccon.cpp b/src/PJ_ccon.cpp new file mode 100644 index 00000000..6ff2d3b1 --- /dev/null +++ b/src/PJ_ccon.cpp @@ -0,0 +1,107 @@ +/****************************************************************************** + * Copyright (c) 2017, Lukasz Komsta + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +#define EPS10 1e-10 + +struct pj_opaque { + double phi1; + double ctgphi1; + double sinphi1; + double cosphi1; + double *en; +}; + +PROJ_HEAD(ccon, "Central Conic") + "\n\tCentral Conic, Sph\n\tlat_1="; + + + +static XY forward (LP lp, PJ *P) { + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double r; + + r = Q->ctgphi1 - tan(lp.phi - Q->phi1); + xy.x = r * sin(lp.lam * Q->sinphi1); + xy.y = Q->ctgphi1 - r * cos(lp.lam * Q->sinphi1); + + return xy; +} + + +static LP inverse (XY xy, PJ *P) { + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y = Q->ctgphi1 - xy.y; + lp.phi = Q->phi1 - atan(hypot(xy.x,xy.y) - Q->ctgphi1); + lp.lam = atan2(xy.x,xy.y)/Q->sinphi1; + + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(ccon) { + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (fabs(Q->phi1) < EPS10) + return destructor (P, PJD_ERR_LAT1_IS_ZERO); + + if (!(Q->en = pj_enfn(P->es))) + return destructor(P, ENOMEM); + + Q->sinphi1 = sin(Q->phi1); + Q->cosphi1 = cos(Q->phi1); + Q->ctgphi1 = Q->cosphi1/Q->sinphi1; + + + P->inv = inverse; + P->fwd = forward; + + return P; +} + + diff --git a/src/PJ_cea.c b/src/PJ_cea.c deleted file mode 100644 index e05e524b..00000000 --- a/src/PJ_cea.c +++ /dev/null @@ -1,101 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -struct pj_opaque { - double qp; - double *apa; -}; - -PROJ_HEAD(cea, "Equal Area Cylindrical") "\n\tCyl, Sph&Ell\n\tlat_ts="; -# define EPS 1e-10 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - xy.x = P->k0 * lp.lam; - xy.y = 0.5 * pj_qsfn (sin (lp.phi), P->e, P->one_es) / P->k0; - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - xy.x = P->k0 * lp.lam; - xy.y = sin(lp.phi) / P->k0; - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = pj_authlat(asin( 2. * xy.y * P->k0 / P->opaque->qp), P->opaque->apa); - lp.lam = xy.x / P->k0; - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double t; - - if ((t = fabs(xy.y *= P->k0)) - EPS <= 1.) { - if (t >= 1.) - lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; - else - lp.phi = asin(xy.y); - lp.lam = xy.x / P->k0; - } else { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - return (lp); -} - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->apa); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(cea) { - double t = 0.0; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - - if (pj_param(P->ctx, P->params, "tlat_ts").i) { - P->k0 = cos(t = pj_param(P->ctx, P->params, "rlat_ts").f); - if (P->k0 < 0.) - return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90); - } - if (P->es != 0.0) { - t = sin(t); - P->k0 /= sqrt(1. - P->es * t * t); - P->e = sqrt(P->es); - if (!(Q->apa = pj_authset(P->es))) - return pj_default_destructor(P, ENOMEM); - - Q->qp = pj_qsfn(1., P->e, P->one_es); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - P->inv = s_inverse; - P->fwd = s_forward; - } - - return P; -} diff --git a/src/PJ_cea.cpp b/src/PJ_cea.cpp new file mode 100644 index 00000000..ee7eebf5 --- /dev/null +++ b/src/PJ_cea.cpp @@ -0,0 +1,101 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +struct pj_opaque { + double qp; + double *apa; +}; + +PROJ_HEAD(cea, "Equal Area Cylindrical") "\n\tCyl, Sph&Ell\n\tlat_ts="; +# define EPS 1e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + xy.x = P->k0 * lp.lam; + xy.y = 0.5 * pj_qsfn (sin (lp.phi), P->e, P->one_es) / P->k0; + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = P->k0 * lp.lam; + xy.y = sin(lp.phi) / P->k0; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = pj_authlat(asin( 2. * xy.y * P->k0 / static_cast(P->opaque)->qp), static_cast(P->opaque)->apa); + lp.lam = xy.x / P->k0; + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + if ((t = fabs(xy.y *= P->k0)) - EPS <= 1.) { + if (t >= 1.) + lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; + else + lp.phi = asin(xy.y); + lp.lam = xy.x / P->k0; + } else { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + return (lp); +} + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->apa); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(cea) { + double t = 0.0; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + + if (pj_param(P->ctx, P->params, "tlat_ts").i) { + P->k0 = cos(t = pj_param(P->ctx, P->params, "rlat_ts").f); + if (P->k0 < 0.) + return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90); + } + if (P->es != 0.0) { + t = sin(t); + P->k0 /= sqrt(1. - P->es * t * t); + P->e = sqrt(P->es); + if (!(Q->apa = pj_authset(P->es))) + return pj_default_destructor(P, ENOMEM); + + Q->qp = pj_qsfn(1., P->e, P->one_es); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} diff --git a/src/PJ_chamb.c b/src/PJ_chamb.c deleted file mode 100644 index 6951d6a1..00000000 --- a/src/PJ_chamb.c +++ /dev/null @@ -1,139 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -typedef struct { double r, Az; } VECT; -struct pj_opaque { - struct { /* control point data */ - double phi, lam; - double cosphi, sinphi; - VECT v; - XY p; - double Az; - } c[3]; - XY p; - double beta_0, beta_1, beta_2; -}; - -PROJ_HEAD(chamb, "Chamberlin Trimetric") "\n\tMisc Sph, no inv" -"\n\tlat_1= lon_1= lat_2= lon_2= lat_3= lon_3="; - -#include -#define THIRD 0.333333333333333333 -#define TOL 1e-9 - -/* distance and azimuth from point 1 to point 2 */ -static VECT vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) { - VECT v; - double cdl, dp, dl; - - cdl = cos(dlam); - if (fabs(dphi) > 1. || fabs(dlam) > 1.) - v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl); - else { /* more accurate for smaller distances */ - dp = sin(.5 * dphi); - dl = sin(.5 * dlam); - v.r = 2. * aasin(ctx,sqrt(dp * dp + c1 * c2 * dl * dl)); - } - if (fabs(v.r) > TOL) - v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl); - else - v.r = v.Az = 0.; - return v; -} - -/* law of cosines */ -static double lc(projCtx ctx, double b,double c,double a) { - return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c)); -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy; - struct pj_opaque *Q = P->opaque; - double sinphi, cosphi, a; - VECT v[3]; - int i, j; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - for (i = 0; i < 3; ++i) { /* dist/azimiths from control */ - v[i] = vect(P->ctx, lp.phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, - cosphi, sinphi, lp.lam - Q->c[i].lam); - if (v[i].r == 0.0) - break; - v[i].Az = adjlon(v[i].Az - Q->c[i].v.Az); - } - if (i < 3) /* current point at control point */ - xy = Q->c[i].p; - else { /* point mean of intersepts */ - xy = Q->p; - for (i = 0; i < 3; ++i) { - j = i == 2 ? 0 : i + 1; - a = lc(P->ctx,Q->c[i].v.r, v[i].r, v[j].r); - if (v[i].Az < 0.) - a = -a; - if (! i) { /* coord comp unique to each arc */ - xy.x += v[i].r * cos(a); - xy.y -= v[i].r * sin(a); - } else if (i == 1) { - a = Q->beta_1 - a; - xy.x -= v[i].r * cos(a); - xy.y -= v[i].r * sin(a); - } else { - a = Q->beta_2 - a; - xy.x += v[i].r * cos(a); - xy.y += v[i].r * sin(a); - } - } - xy.x *= THIRD; /* mean of arc intercepts */ - xy.y *= THIRD; - } - return xy; -} - - - -PJ *PROJECTION(chamb) { - int i, j; - char line[10]; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - - for (i = 0; i < 3; ++i) { /* get control point locations */ - (void)sprintf(line, "rlat_%d", i+1); - Q->c[i].phi = pj_param(P->ctx, P->params, line).f; - (void)sprintf(line, "rlon_%d", i+1); - Q->c[i].lam = pj_param(P->ctx, P->params, line).f; - Q->c[i].lam = adjlon(Q->c[i].lam - P->lam0); - Q->c[i].cosphi = cos(Q->c[i].phi); - Q->c[i].sinphi = sin(Q->c[i].phi); - } - for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */ - j = i == 2 ? 0 : i + 1; - Q->c[i].v = vect(P->ctx,Q->c[j].phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, - Q->c[j].cosphi, Q->c[j].sinphi, Q->c[j].lam - Q->c[i].lam); - if (Q->c[i].v.r == 0.0) - return pj_default_destructor (P, PJD_ERR_CONTROL_POINT_NO_DIST); - /* co-linearity problem ignored for now */ - } - Q->beta_0 = lc(P->ctx,Q->c[0].v.r, Q->c[2].v.r, Q->c[1].v.r); - Q->beta_1 = lc(P->ctx,Q->c[0].v.r, Q->c[1].v.r, Q->c[2].v.r); - Q->beta_2 = M_PI - Q->beta_0; - Q->p.y = 2. * (Q->c[0].p.y = Q->c[1].p.y = Q->c[2].v.r * sin(Q->beta_0)); - Q->c[2].p.y = 0.; - Q->c[0].p.x = - (Q->c[1].p.x = 0.5 * Q->c[0].v.r); - Q->p.x = Q->c[2].p.x = Q->c[0].p.x + Q->c[2].v.r * cos(Q->beta_0); - - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_chamb.cpp b/src/PJ_chamb.cpp new file mode 100644 index 00000000..8d8f05ee --- /dev/null +++ b/src/PJ_chamb.cpp @@ -0,0 +1,139 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +typedef struct { double r, Az; } VECT; +struct pj_opaque { + struct { /* control point data */ + double phi, lam; + double cosphi, sinphi; + VECT v; + XY p; + double Az; + } c[3]; + XY p; + double beta_0, beta_1, beta_2; +}; + +PROJ_HEAD(chamb, "Chamberlin Trimetric") "\n\tMisc Sph, no inv" +"\n\tlat_1= lon_1= lat_2= lon_2= lat_3= lon_3="; + +#include +#define THIRD 0.333333333333333333 +#define TOL 1e-9 + +/* distance and azimuth from point 1 to point 2 */ +static VECT vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) { + VECT v; + double cdl, dp, dl; + + cdl = cos(dlam); + if (fabs(dphi) > 1. || fabs(dlam) > 1.) + v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl); + else { /* more accurate for smaller distances */ + dp = sin(.5 * dphi); + dl = sin(.5 * dlam); + v.r = 2. * aasin(ctx,sqrt(dp * dp + c1 * c2 * dl * dl)); + } + if (fabs(v.r) > TOL) + v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl); + else + v.r = v.Az = 0.; + return v; +} + +/* law of cosines */ +static double lc(projCtx ctx, double b,double c,double a) { + return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c)); +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy; + struct pj_opaque *Q = static_cast(P->opaque); + double sinphi, cosphi, a; + VECT v[3]; + int i, j; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + for (i = 0; i < 3; ++i) { /* dist/azimiths from control */ + v[i] = vect(P->ctx, lp.phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, + cosphi, sinphi, lp.lam - Q->c[i].lam); + if (v[i].r == 0.0) + break; + v[i].Az = adjlon(v[i].Az - Q->c[i].v.Az); + } + if (i < 3) /* current point at control point */ + xy = Q->c[i].p; + else { /* point mean of intersepts */ + xy = Q->p; + for (i = 0; i < 3; ++i) { + j = i == 2 ? 0 : i + 1; + a = lc(P->ctx,Q->c[i].v.r, v[i].r, v[j].r); + if (v[i].Az < 0.) + a = -a; + if (! i) { /* coord comp unique to each arc */ + xy.x += v[i].r * cos(a); + xy.y -= v[i].r * sin(a); + } else if (i == 1) { + a = Q->beta_1 - a; + xy.x -= v[i].r * cos(a); + xy.y -= v[i].r * sin(a); + } else { + a = Q->beta_2 - a; + xy.x += v[i].r * cos(a); + xy.y += v[i].r * sin(a); + } + } + xy.x *= THIRD; /* mean of arc intercepts */ + xy.y *= THIRD; + } + return xy; +} + + + +PJ *PROJECTION(chamb) { + int i, j; + char line[10]; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + + for (i = 0; i < 3; ++i) { /* get control point locations */ + (void)sprintf(line, "rlat_%d", i+1); + Q->c[i].phi = pj_param(P->ctx, P->params, line).f; + (void)sprintf(line, "rlon_%d", i+1); + Q->c[i].lam = pj_param(P->ctx, P->params, line).f; + Q->c[i].lam = adjlon(Q->c[i].lam - P->lam0); + Q->c[i].cosphi = cos(Q->c[i].phi); + Q->c[i].sinphi = sin(Q->c[i].phi); + } + for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */ + j = i == 2 ? 0 : i + 1; + Q->c[i].v = vect(P->ctx,Q->c[j].phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, + Q->c[j].cosphi, Q->c[j].sinphi, Q->c[j].lam - Q->c[i].lam); + if (Q->c[i].v.r == 0.0) + return pj_default_destructor (P, PJD_ERR_CONTROL_POINT_NO_DIST); + /* co-linearity problem ignored for now */ + } + Q->beta_0 = lc(P->ctx,Q->c[0].v.r, Q->c[2].v.r, Q->c[1].v.r); + Q->beta_1 = lc(P->ctx,Q->c[0].v.r, Q->c[1].v.r, Q->c[2].v.r); + Q->beta_2 = M_PI - Q->beta_0; + Q->p.y = 2. * (Q->c[0].p.y = Q->c[1].p.y = Q->c[2].v.r * sin(Q->beta_0)); + Q->c[2].p.y = 0.; + Q->c[0].p.x = - (Q->c[1].p.x = 0.5 * Q->c[0].v.r); + Q->p.x = Q->c[2].p.x = Q->c[0].p.x + Q->c[2].v.r * cos(Q->beta_0); + + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_collg.c b/src/PJ_collg.c deleted file mode 100644 index 7904de29..00000000 --- a/src/PJ_collg.c +++ /dev/null @@ -1,53 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(collg, "Collignon") "\n\tPCyl, Sph"; -#define FXC 1.12837916709551257390 -#define FYC 1.77245385090551602729 -#define ONEEPS 1.0000001 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - if ((xy.y = 1. - sin(lp.phi)) <= 0.) - xy.y = 0.; - else - xy.y = sqrt(xy.y); - xy.x = FXC * lp.lam * xy.y; - xy.y = FYC * (1. - xy.y); - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = xy.y / FYC - 1.; - if (fabs(lp.phi = 1. - lp.phi * lp.phi) < 1.) - lp.phi = asin(lp.phi); - else if (fabs(lp.phi) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - } - - if ((lp.lam = 1. - sin(lp.phi)) <= 0.) - lp.lam = 0.; - else - lp.lam = xy.x / (FXC * sqrt(lp.lam)); - return (lp); -} - - -PJ *PROJECTION(collg) { - P->es = 0.0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_collg.cpp b/src/PJ_collg.cpp new file mode 100644 index 00000000..7904de29 --- /dev/null +++ b/src/PJ_collg.cpp @@ -0,0 +1,53 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(collg, "Collignon") "\n\tPCyl, Sph"; +#define FXC 1.12837916709551257390 +#define FYC 1.77245385090551602729 +#define ONEEPS 1.0000001 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + if ((xy.y = 1. - sin(lp.phi)) <= 0.) + xy.y = 0.; + else + xy.y = sqrt(xy.y); + xy.x = FXC * lp.lam * xy.y; + xy.y = FYC * (1. - xy.y); + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = xy.y / FYC - 1.; + if (fabs(lp.phi = 1. - lp.phi * lp.phi) < 1.) + lp.phi = asin(lp.phi); + else if (fabs(lp.phi) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + } + + if ((lp.lam = 1. - sin(lp.phi)) <= 0.) + lp.lam = 0.; + else + lp.lam = xy.x / (FXC * sqrt(lp.lam)); + return (lp); +} + + +PJ *PROJECTION(collg) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_comill.c b/src/PJ_comill.c deleted file mode 100644 index b6e0192e..00000000 --- a/src/PJ_comill.c +++ /dev/null @@ -1,84 +0,0 @@ -/* -The Compact Miller projection was designed by Tom Patterson, US National -Park Service, in 2014. The polynomial equation was developed by Bojan -Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric -Sciences, Oregon State University. -Port to PROJ.4 by Bojan Savric, 4 April 2016 -*/ - -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(comill, "Compact Miller") "\n\tCyl, Sph"; - -#define K1 0.9902 -#define K2 0.1604 -#define K3 -0.03054 -#define C1 K1 -#define C2 (3 * K2) -#define C3 (5 * K3) -#define EPS 1e-11 -#define MAX_Y (0.6000207669862655 * M_PI) -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double lat_sq; - - (void) P; /* silence unused parameter warnings */ - - lat_sq = lp.phi * lp.phi; - xy.x = lp.lam; - xy.y = lp.phi * (K1 + lat_sq * (K2 + K3 * lat_sq)); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double yc, tol, y2, f, fder; - int i; - - (void) P; /* silence unused parameter warnings */ - - /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } - - /* latitude */ - yc = xy.y; - for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ - y2 = yc * yc; - f = (yc * (K1 + y2 * (K2 + K3 * y2))) - xy.y; - fder = C1 + y2 * (C2 + C3 * y2); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - if( i == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - lp.phi = yc; - - /* longitude */ - lp.lam = xy.x; - - return lp; -} - - -PJ *PROJECTION(comill) { - P->es = 0; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_comill.cpp b/src/PJ_comill.cpp new file mode 100644 index 00000000..b6e0192e --- /dev/null +++ b/src/PJ_comill.cpp @@ -0,0 +1,84 @@ +/* +The Compact Miller projection was designed by Tom Patterson, US National +Park Service, in 2014. The polynomial equation was developed by Bojan +Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric +Sciences, Oregon State University. +Port to PROJ.4 by Bojan Savric, 4 April 2016 +*/ + +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(comill, "Compact Miller") "\n\tCyl, Sph"; + +#define K1 0.9902 +#define K2 0.1604 +#define K3 -0.03054 +#define C1 K1 +#define C2 (3 * K2) +#define C3 (5 * K3) +#define EPS 1e-11 +#define MAX_Y (0.6000207669862655 * M_PI) +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double lat_sq; + + (void) P; /* silence unused parameter warnings */ + + lat_sq = lp.phi * lp.phi; + xy.x = lp.lam; + xy.y = lp.phi * (K1 + lat_sq * (K2 + K3 * lat_sq)); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, f, fder; + int i; + + (void) P; /* silence unused parameter warnings */ + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + /* latitude */ + yc = xy.y; + for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ + y2 = yc * yc; + f = (yc * (K1 + y2 * (K2 + K3 * y2))) - xy.y; + fder = C1 + y2 * (C2 + C3 * y2); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + if( i == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + lp.phi = yc; + + /* longitude */ + lp.lam = xy.x; + + return lp; +} + + +PJ *PROJECTION(comill) { + P->es = 0; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_crast.c b/src/PJ_crast.c deleted file mode 100644 index 4e4dee8b..00000000 --- a/src/PJ_crast.c +++ /dev/null @@ -1,40 +0,0 @@ -#define PJ_LIB__ -#include - -#include "projects.h" - -PROJ_HEAD(crast, "Craster Parabolic (Putnins P4)") "\n\tPCyl, Sph"; - -#define XM 0.97720502380583984317 -#define RXM 1.02332670794648848847 -#define YM 3.06998012383946546542 -#define RYM 0.32573500793527994772 -#define THIRD 0.333333333333333333 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - lp.phi *= THIRD; - xy.x = XM * lp.lam * (2. * cos(lp.phi + lp.phi) - 1.); - xy.y = YM * sin(lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - lp.phi = 3. * asin(xy.y * RYM); - lp.lam = xy.x * RXM / (2. * cos((lp.phi + lp.phi) * THIRD) - 1); - return lp; -} - - -PJ *PROJECTION(crast) { - P->es = 0.0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_crast.cpp b/src/PJ_crast.cpp new file mode 100644 index 00000000..4e4dee8b --- /dev/null +++ b/src/PJ_crast.cpp @@ -0,0 +1,40 @@ +#define PJ_LIB__ +#include + +#include "projects.h" + +PROJ_HEAD(crast, "Craster Parabolic (Putnins P4)") "\n\tPCyl, Sph"; + +#define XM 0.97720502380583984317 +#define RXM 1.02332670794648848847 +#define YM 3.06998012383946546542 +#define RYM 0.32573500793527994772 +#define THIRD 0.333333333333333333 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + lp.phi *= THIRD; + xy.x = XM * lp.lam * (2. * cos(lp.phi + lp.phi) - 1.); + xy.y = YM * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + lp.phi = 3. * asin(xy.y * RYM); + lp.lam = xy.x * RXM / (2. * cos((lp.phi + lp.phi) * THIRD) - 1); + return lp; +} + + +PJ *PROJECTION(crast) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_deformation.c b/src/PJ_deformation.c deleted file mode 100644 index 5511eed4..00000000 --- a/src/PJ_deformation.c +++ /dev/null @@ -1,323 +0,0 @@ -/*********************************************************************** - - Kinematic datum shifting utilizing a deformation model - - Kristian Evers, 2017-10-29 - -************************************************************************ - -Perform datum shifts by means of a deformation/velocity model. - - X_out = X_in + (T_ct - T_obs)*DX - Y_out = Y_in + (T_ct - T_obs)*DY - Z_out = Z_in + (T_ct - T_obs)*DZ - - -The deformation operation takes cartesian coordinates as input and -returns cartesian coordinates as well. - -Corrections in the gridded model are in east, north, up (ENU) space. -Hence the input coordinates needs to be converted to ENU-space when -searching for corrections in the grid. The corrections are then converted -to cartesian XYZ-space and applied to the input coordinates (also in -cartesian space). - -A full deformation model is described by two grids, one for the horizontal -components and one for the vertical component. The horizontal grid is -stored in CTable/CTable2 and the vertical grid is stored in the GTX -format. The NTv2 format should not be used for this purpose since grid- -values are scaled upon reading. Both grids are expected to contain -grid-values in units of mm/year in ENU-space. - -************************************************************************ -* Copyright (c) 2017, Kristian Evers -* -* 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. -* -***********************************************************************/ -#define PJ_LIB__ -#include -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -PROJ_HEAD(deformation, "Kinematic grid shift"); - -#define TOL 1e-8 -#define MAX_ITERATIONS 10 - -struct pj_opaque { - double t_obs; - double t_epoch; - PJ *cart; -}; - -/********************************************************************************/ -static XYZ get_grid_shift(PJ* P, XYZ cartesian) { -/******************************************************************************** - Read correction values from grid. The cartesian input coordinates are - converted to geodetic coordinates in order look up the correction values - in the grid. Once the grid corrections are read we need to convert them - from ENU-space to cartesian XYZ-space. ENU -> XYZ formula described in: - - Nørbech, T., et al, 2003(?), "Transformation from a Common Nordic Reference - Frame to ETRS89 in Denmark, Finland, Norway, and Sweden – status report" - -********************************************************************************/ - PJ_COORD geodetic, shift, temp; - double sp, cp, sl, cl; - int previous_errno = proj_errno_reset(P); - - /* cartesian to geodetic */ - geodetic.lpz = pj_inv3d(cartesian, P->opaque->cart); - - /* look up correction values in grids */ - shift.lp = proj_hgrid_value(P, geodetic.lp); - shift.enu.u = proj_vgrid_value(P, geodetic.lp); - - if (proj_errno(P) == PJD_ERR_GRID_AREA) - proj_log_debug(P, "deformation: coordinate (%.3f, %.3f) outside deformation model", - proj_todeg(geodetic.lp.lam), proj_todeg(geodetic.lp.phi)); - - /* grid values are stored as mm/yr, we need m/yr */ - shift.xyz.x /= 1000; - shift.xyz.y /= 1000; - shift.xyz.z /= 1000; - - /* pre-calc cosines and sines */ - sp = sin(geodetic.lp.phi); - cp = cos(geodetic.lp.phi); - sl = sin(geodetic.lp.lam); - cl = cos(geodetic.lp.lam); - - /* ENU -> XYZ */ - temp.xyz.x = -sp*cl*shift.enu.n - sl*shift.enu.e + cp*cl*shift.enu.u; - temp.xyz.y = -sp*sl*shift.enu.n + cl*shift.enu.e + cp*sl*shift.enu.u; - temp.xyz.z = cp*shift.enu.n + sp*shift.enu.u; - - shift.xyz = temp.xyz; - - proj_errno_restore(P, previous_errno); - - return shift.xyz; -} - -/********************************************************************************/ -static XYZ reverse_shift(PJ *P, XYZ input, double dt) { -/******************************************************************************** - Iteratively determine the reverse grid shift correction values. -*********************************************************************************/ - XYZ out, delta, dif; - double z0; - int i = MAX_ITERATIONS; - - delta = get_grid_shift(P, input); - - /* Store the origial z shift for later application */ - z0 = delta.z; - - /* When iterating to find the best horizontal coordinate we also carry */ - /* along the z-component, since we need it for the cartesian -> geodetic */ - /* conversion. The z-component adjustment is overwritten with z0 after */ - /* the loop has finished. */ - out.x = input.x - dt*delta.x; - out.y = input.y - dt*delta.y; - out.z = input.z + dt*delta.z; - - do { - delta = get_grid_shift(P, out); - - if (delta.x == HUGE_VAL) - break; - - dif.x = out.x + dt*delta.x - input.x; - dif.y = out.y + dt*delta.y - input.y; - dif.z = out.z - dt*delta.z - input.z; - out.x += dif.x; - out.y += dif.y; - out.z += dif.z; - - } while ( --i && hypot(dif.x, dif.y) > TOL ); - - out.z = input.z - dt*z0; - - return out; -} - -static XYZ forward_3d(LPZ lpz, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - PJ_COORD out, in; - XYZ shift; - double dt = 0.0; - in.lpz = lpz; - out = in; - - if (Q->t_obs != HUGE_VAL) { - dt = Q->t_epoch - Q->t_obs; - } else { - out = proj_coord_error(); /* in the 3D case +t_obs must be specified */ - proj_log_debug(P, "deformation: +t_obs must be specified"); - return out.xyz; - } - - shift = get_grid_shift(P, in.xyz); - - out.xyz.x += dt * shift.x; - out.xyz.y += dt * shift.y; - out.xyz.z += dt * shift.z; - - return out.xyz; -} - - -static PJ_COORD forward_4d(PJ_COORD in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - double dt; - XYZ shift; - PJ_COORD out = in; - - if (Q->t_obs != HUGE_VAL) { - dt = Q->t_epoch - Q->t_obs; - } else { - dt = Q->t_epoch - in.xyzt.t; - } - - shift = get_grid_shift(P, in.xyz); - - out.xyzt.x += dt*shift.x; - out.xyzt.y += dt*shift.y; - out.xyzt.z += dt*shift.z; - - - return out; -} - - -static LPZ reverse_3d(XYZ in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - PJ_COORD out; - double dt = 0.0; - out.xyz = in; - - if (Q->t_obs != HUGE_VAL) { - dt = Q->t_epoch - Q->t_obs; - } else { - out = proj_coord_error(); /* in the 3D case +t_obs must be specified */ - proj_log_debug(P, "deformation: +t_obs must be specified"); - return out.lpz; - } - - out.xyz = reverse_shift(P, in, dt); - - return out.lpz; -} - -static PJ_COORD reverse_4d(PJ_COORD in, PJ *P) { - struct pj_opaque *Q = (struct pj_opaque *) P->opaque; - PJ_COORD out = in; - double dt; - - - if (Q->t_obs != HUGE_VAL) { - dt = Q->t_epoch - Q->t_obs; - } else { - dt = Q->t_epoch - in.xyzt.t; - } - - out.xyz = reverse_shift(P, in.xyz, dt); - return out; -} - -static void *destructor(PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - if (P->opaque->cart) - P->opaque->cart->destructor (P->opaque->cart, errlev); - - return pj_default_destructor(P, errlev); -} - - -PJ *TRANSFORMATION(deformation,1) { - int has_xy_grids = 0; - int has_z_grids = 0; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return destructor(P, ENOMEM); - P->opaque = (void *) Q; - - Q->cart = proj_create(P->ctx, "+proj=cart"); - if (Q->cart == 0) - return destructor(P, ENOMEM); - - /* inherit ellipsoid definition from P to Q->cart */ - pj_inherit_ellipsoid_def (P, Q->cart); - - has_xy_grids = pj_param(P->ctx, P->params, "txy_grids").i; - has_z_grids = pj_param(P->ctx, P->params, "tz_grids").i; - - /* Build gridlists. Both horizontal and vertical grids are mandatory. */ - if (!has_xy_grids || !has_z_grids) { - proj_log_error(P, "deformation: Both +xy_grids and +z_grids should be specified."); - return destructor(P, PJD_ERR_NO_ARGS ); - } - - proj_hgrid_init(P, "xy_grids"); - if (proj_errno(P)) { - proj_log_error(P, "deformation: could not find requested xy_grid(s)."); - return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - proj_vgrid_init(P, "z_grids"); - if (proj_errno(P)) { - proj_log_error(P, "deformation: could not find requested z_grid(s)."); - return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - Q->t_obs = HUGE_VAL; - if (pj_param(P->ctx, P->params, "tt_obs").i) { - Q->t_obs = pj_param(P->ctx, P->params, "dt_obs").f; - } - - if (pj_param(P->ctx, P->params, "tt_epoch").i) { - Q->t_epoch = pj_param(P->ctx, P->params, "dt_epoch").f; - } else { - proj_log_error(P, "deformation: +t_epoch parameter missing."); - return destructor(P, PJD_ERR_MISSING_ARGS); - } - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = 0; - P->inv = 0; - - P->left = PJ_IO_UNITS_CARTESIAN; - P->right = PJ_IO_UNITS_CARTESIAN; - P->destructor = destructor; - - return P; -} - diff --git a/src/PJ_deformation.cpp b/src/PJ_deformation.cpp new file mode 100644 index 00000000..1398f5fd --- /dev/null +++ b/src/PJ_deformation.cpp @@ -0,0 +1,323 @@ +/*********************************************************************** + + Kinematic datum shifting utilizing a deformation model + + Kristian Evers, 2017-10-29 + +************************************************************************ + +Perform datum shifts by means of a deformation/velocity model. + + X_out = X_in + (T_ct - T_obs)*DX + Y_out = Y_in + (T_ct - T_obs)*DY + Z_out = Z_in + (T_ct - T_obs)*DZ + + +The deformation operation takes cartesian coordinates as input and +returns cartesian coordinates as well. + +Corrections in the gridded model are in east, north, up (ENU) space. +Hence the input coordinates needs to be converted to ENU-space when +searching for corrections in the grid. The corrections are then converted +to cartesian XYZ-space and applied to the input coordinates (also in +cartesian space). + +A full deformation model is described by two grids, one for the horizontal +components and one for the vertical component. The horizontal grid is +stored in CTable/CTable2 and the vertical grid is stored in the GTX +format. The NTv2 format should not be used for this purpose since grid- +values are scaled upon reading. Both grids are expected to contain +grid-values in units of mm/year in ENU-space. + +************************************************************************ +* Copyright (c) 2017, Kristian Evers +* +* 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. +* +***********************************************************************/ +#define PJ_LIB__ +#include +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +PROJ_HEAD(deformation, "Kinematic grid shift"); + +#define TOL 1e-8 +#define MAX_ITERATIONS 10 + +struct pj_opaque { + double t_obs; + double t_epoch; + PJ *cart; +}; + +/********************************************************************************/ +static XYZ get_grid_shift(PJ* P, XYZ cartesian) { +/******************************************************************************** + Read correction values from grid. The cartesian input coordinates are + converted to geodetic coordinates in order look up the correction values + in the grid. Once the grid corrections are read we need to convert them + from ENU-space to cartesian XYZ-space. ENU -> XYZ formula described in: + + Nørbech, T., et al, 2003(?), "Transformation from a Common Nordic Reference + Frame to ETRS89 in Denmark, Finland, Norway, and Sweden – status report" + +********************************************************************************/ + PJ_COORD geodetic, shift, temp; + double sp, cp, sl, cl; + int previous_errno = proj_errno_reset(P); + + /* cartesian to geodetic */ + geodetic.lpz = pj_inv3d(cartesian, static_cast(P->opaque)->cart); + + /* look up correction values in grids */ + shift.lp = proj_hgrid_value(P, geodetic.lp); + shift.enu.u = proj_vgrid_value(P, geodetic.lp); + + if (proj_errno(P) == PJD_ERR_GRID_AREA) + proj_log_debug(P, "deformation: coordinate (%.3f, %.3f) outside deformation model", + proj_todeg(geodetic.lp.lam), proj_todeg(geodetic.lp.phi)); + + /* grid values are stored as mm/yr, we need m/yr */ + shift.xyz.x /= 1000; + shift.xyz.y /= 1000; + shift.xyz.z /= 1000; + + /* pre-calc cosines and sines */ + sp = sin(geodetic.lp.phi); + cp = cos(geodetic.lp.phi); + sl = sin(geodetic.lp.lam); + cl = cos(geodetic.lp.lam); + + /* ENU -> XYZ */ + temp.xyz.x = -sp*cl*shift.enu.n - sl*shift.enu.e + cp*cl*shift.enu.u; + temp.xyz.y = -sp*sl*shift.enu.n + cl*shift.enu.e + cp*sl*shift.enu.u; + temp.xyz.z = cp*shift.enu.n + sp*shift.enu.u; + + shift.xyz = temp.xyz; + + proj_errno_restore(P, previous_errno); + + return shift.xyz; +} + +/********************************************************************************/ +static XYZ reverse_shift(PJ *P, XYZ input, double dt) { +/******************************************************************************** + Iteratively determine the reverse grid shift correction values. +*********************************************************************************/ + XYZ out, delta, dif; + double z0; + int i = MAX_ITERATIONS; + + delta = get_grid_shift(P, input); + + /* Store the origial z shift for later application */ + z0 = delta.z; + + /* When iterating to find the best horizontal coordinate we also carry */ + /* along the z-component, since we need it for the cartesian -> geodetic */ + /* conversion. The z-component adjustment is overwritten with z0 after */ + /* the loop has finished. */ + out.x = input.x - dt*delta.x; + out.y = input.y - dt*delta.y; + out.z = input.z + dt*delta.z; + + do { + delta = get_grid_shift(P, out); + + if (delta.x == HUGE_VAL) + break; + + dif.x = out.x + dt*delta.x - input.x; + dif.y = out.y + dt*delta.y - input.y; + dif.z = out.z - dt*delta.z - input.z; + out.x += dif.x; + out.y += dif.y; + out.z += dif.z; + + } while ( --i && hypot(dif.x, dif.y) > TOL ); + + out.z = input.z - dt*z0; + + return out; +} + +static XYZ forward_3d(LPZ lpz, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + PJ_COORD out, in; + XYZ shift; + double dt = 0.0; + in.lpz = lpz; + out = in; + + if (Q->t_obs != HUGE_VAL) { + dt = Q->t_epoch - Q->t_obs; + } else { + out = proj_coord_error(); /* in the 3D case +t_obs must be specified */ + proj_log_debug(P, "deformation: +t_obs must be specified"); + return out.xyz; + } + + shift = get_grid_shift(P, in.xyz); + + out.xyz.x += dt * shift.x; + out.xyz.y += dt * shift.y; + out.xyz.z += dt * shift.z; + + return out.xyz; +} + + +static PJ_COORD forward_4d(PJ_COORD in, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + double dt; + XYZ shift; + PJ_COORD out = in; + + if (Q->t_obs != HUGE_VAL) { + dt = Q->t_epoch - Q->t_obs; + } else { + dt = Q->t_epoch - in.xyzt.t; + } + + shift = get_grid_shift(P, in.xyz); + + out.xyzt.x += dt*shift.x; + out.xyzt.y += dt*shift.y; + out.xyzt.z += dt*shift.z; + + + return out; +} + + +static LPZ reverse_3d(XYZ in, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + PJ_COORD out; + double dt = 0.0; + out.xyz = in; + + if (Q->t_obs != HUGE_VAL) { + dt = Q->t_epoch - Q->t_obs; + } else { + out = proj_coord_error(); /* in the 3D case +t_obs must be specified */ + proj_log_debug(P, "deformation: +t_obs must be specified"); + return out.lpz; + } + + out.xyz = reverse_shift(P, in, dt); + + return out.lpz; +} + +static PJ_COORD reverse_4d(PJ_COORD in, PJ *P) { + struct pj_opaque *Q = (struct pj_opaque *) P->opaque; + PJ_COORD out = in; + double dt; + + + if (Q->t_obs != HUGE_VAL) { + dt = Q->t_epoch - Q->t_obs; + } else { + dt = Q->t_epoch - in.xyzt.t; + } + + out.xyz = reverse_shift(P, in.xyz, dt); + return out; +} + +static PJ *destructor(PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + if (static_cast(P->opaque)->cart) + static_cast(P->opaque)->cart->destructor (static_cast(P->opaque)->cart, errlev); + + return pj_default_destructor(P, errlev); +} + + +PJ *TRANSFORMATION(deformation,1) { + int has_xy_grids = 0; + int has_z_grids = 0; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return destructor(P, ENOMEM); + P->opaque = (void *) Q; + + Q->cart = proj_create(P->ctx, "+proj=cart"); + if (Q->cart == 0) + return destructor(P, ENOMEM); + + /* inherit ellipsoid definition from P to Q->cart */ + pj_inherit_ellipsoid_def (P, Q->cart); + + has_xy_grids = pj_param(P->ctx, P->params, "txy_grids").i; + has_z_grids = pj_param(P->ctx, P->params, "tz_grids").i; + + /* Build gridlists. Both horizontal and vertical grids are mandatory. */ + if (!has_xy_grids || !has_z_grids) { + proj_log_error(P, "deformation: Both +xy_grids and +z_grids should be specified."); + return destructor(P, PJD_ERR_NO_ARGS ); + } + + proj_hgrid_init(P, "xy_grids"); + if (proj_errno(P)) { + proj_log_error(P, "deformation: could not find requested xy_grid(s)."); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + proj_vgrid_init(P, "z_grids"); + if (proj_errno(P)) { + proj_log_error(P, "deformation: could not find requested z_grid(s)."); + return destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + Q->t_obs = HUGE_VAL; + if (pj_param(P->ctx, P->params, "tt_obs").i) { + Q->t_obs = pj_param(P->ctx, P->params, "dt_obs").f; + } + + if (pj_param(P->ctx, P->params, "tt_epoch").i) { + Q->t_epoch = pj_param(P->ctx, P->params, "dt_epoch").f; + } else { + proj_log_error(P, "deformation: +t_epoch parameter missing."); + return destructor(P, PJD_ERR_MISSING_ARGS); + } + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = 0; + P->inv = 0; + + P->left = PJ_IO_UNITS_CARTESIAN; + P->right = PJ_IO_UNITS_CARTESIAN; + P->destructor = destructor; + + return P; +} + diff --git a/src/PJ_denoy.c b/src/PJ_denoy.c deleted file mode 100644 index 5c337c45..00000000 --- a/src/PJ_denoy.c +++ /dev/null @@ -1,32 +0,0 @@ -#define PJ_LIB__ -#include - -#include "projects.h" - -PROJ_HEAD(denoy, "Denoyer Semi-Elliptical") "\n\tPCyl, no inv, Sph"; - -#define C0 0.95 -#define C1 -0.08333333333333333333 -#define C3 0.00166666666666666666 -#define D1 0.9 -#define D5 0.03 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - (void) P; - xy.y = lp.phi; - xy.x = lp.lam; - lp.lam = fabs(lp.lam); - xy.x *= cos((C0 + lp.lam * (C1 + lp.lam * lp.lam * C3)) * - (lp.phi * (D1 + D5 * lp.phi * lp.phi * lp.phi * lp.phi))); - return xy; -} - - -PJ *PROJECTION(denoy) { - P->es = 0.0; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_denoy.cpp b/src/PJ_denoy.cpp new file mode 100644 index 00000000..5c337c45 --- /dev/null +++ b/src/PJ_denoy.cpp @@ -0,0 +1,32 @@ +#define PJ_LIB__ +#include + +#include "projects.h" + +PROJ_HEAD(denoy, "Denoyer Semi-Elliptical") "\n\tPCyl, no inv, Sph"; + +#define C0 0.95 +#define C1 -0.08333333333333333333 +#define C3 0.00166666666666666666 +#define D1 0.9 +#define D5 0.03 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + (void) P; + xy.y = lp.phi; + xy.x = lp.lam; + lp.lam = fabs(lp.lam); + xy.x *= cos((C0 + lp.lam * (C1 + lp.lam * lp.lam * C3)) * + (lp.phi * (D1 + D5 * lp.phi * lp.phi * lp.phi * lp.phi))); + return xy; +} + + +PJ *PROJECTION(denoy) { + P->es = 0.0; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_eck1.c b/src/PJ_eck1.c deleted file mode 100644 index 88a7430c..00000000 --- a/src/PJ_eck1.c +++ /dev/null @@ -1,41 +0,0 @@ -#define PJ_LIB__ -#include - -#include "projects.h" - -PROJ_HEAD(eck1, "Eckert I") "\n\tPCyl, Sph"; -#define FC 0.92131773192356127802 -#define RP 0.31830988618379067154 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = FC * lp.lam * (1. - RP * fabs(lp.phi)); - xy.y = FC * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - - lp.phi = xy.y / FC; - lp.lam = xy.x / (FC * (1. - RP * fabs(lp.phi))); - - return (lp); -} - - - -PJ *PROJECTION(eck1) { - P->es = 0.0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P -; -} diff --git a/src/PJ_eck1.cpp b/src/PJ_eck1.cpp new file mode 100644 index 00000000..88a7430c --- /dev/null +++ b/src/PJ_eck1.cpp @@ -0,0 +1,41 @@ +#define PJ_LIB__ +#include + +#include "projects.h" + +PROJ_HEAD(eck1, "Eckert I") "\n\tPCyl, Sph"; +#define FC 0.92131773192356127802 +#define RP 0.31830988618379067154 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = FC * lp.lam * (1. - RP * fabs(lp.phi)); + xy.y = FC * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.phi = xy.y / FC; + lp.lam = xy.x / (FC * (1. - RP * fabs(lp.phi))); + + return (lp); +} + + + +PJ *PROJECTION(eck1) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P +; +} diff --git a/src/PJ_eck2.c b/src/PJ_eck2.c deleted file mode 100644 index f76ab4ec..00000000 --- a/src/PJ_eck2.c +++ /dev/null @@ -1,56 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(eck2, "Eckert II") "\n\tPCyl, Sph"; - -#define FXC 0.46065886596178063902 -#define FYC 1.44720250911653531871 -#define C13 0.33333333333333333333 -#define ONEEPS 1.0000001 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = FXC * lp.lam * (xy.y = sqrt(4. - 3. * sin(fabs(lp.phi)))); - xy.y = FYC * (2. - xy.y); - if ( lp.phi < 0.) xy.y = -xy.y; - - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - - lp.lam = xy.x / (FXC * ( lp.phi = 2. - fabs(xy.y) / FYC) ); - lp.phi = (4. - lp.phi * lp.phi) * C13; - if (fabs(lp.phi) >= 1.) { - if (fabs(lp.phi) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - } - } else - lp.phi = asin(lp.phi); - if (xy.y < 0) - lp.phi = -lp.phi; - return (lp); -} - - - -PJ *PROJECTION(eck2) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_eck2.cpp b/src/PJ_eck2.cpp new file mode 100644 index 00000000..f76ab4ec --- /dev/null +++ b/src/PJ_eck2.cpp @@ -0,0 +1,56 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(eck2, "Eckert II") "\n\tPCyl, Sph"; + +#define FXC 0.46065886596178063902 +#define FYC 1.44720250911653531871 +#define C13 0.33333333333333333333 +#define ONEEPS 1.0000001 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = FXC * lp.lam * (xy.y = sqrt(4. - 3. * sin(fabs(lp.phi)))); + xy.y = FYC * (2. - xy.y); + if ( lp.phi < 0.) xy.y = -xy.y; + + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = xy.x / (FXC * ( lp.phi = 2. - fabs(xy.y) / FYC) ); + lp.phi = (4. - lp.phi * lp.phi) * C13; + if (fabs(lp.phi) >= 1.) { + if (fabs(lp.phi) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + } + } else + lp.phi = asin(lp.phi); + if (xy.y < 0) + lp.phi = -lp.phi; + return (lp); +} + + + +PJ *PROJECTION(eck2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_eck3.c b/src/PJ_eck3.c deleted file mode 100644 index 8f91c8bb..00000000 --- a/src/PJ_eck3.c +++ /dev/null @@ -1,110 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(eck3, "Eckert III") "\n\tPCyl, Sph"; -PROJ_HEAD(putp1, "Putnins P1") "\n\tPCyl, Sph"; -PROJ_HEAD(wag6, "Wagner VI") "\n\tPCyl, Sph"; -PROJ_HEAD(kav7, "Kavraisky VII") "\n\tPCyl, Sph"; - -struct pj_opaque { - double C_x, C_y, A, B; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y = Q->C_y * lp.phi; - xy.x = Q->C_x * lp.lam * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi)); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double denominator; - - lp.phi = xy.y / Q->C_y; - denominator = (Q->C_x * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi))); - if ( denominator == 0.0) - lp.lam = HUGE_VAL; - else - lp.lam = xy.x / denominator; - return lp; -} - - -static PJ *setup(PJ *P) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} - - -PJ *PROJECTION(eck3) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->C_x = 0.42223820031577120149; - Q->C_y = 0.84447640063154240298; - Q->A = 1.0; - Q->B = 0.4052847345693510857755; - - return setup(P); -} - - -PJ *PROJECTION(kav7) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - /* Defined twice in original code - Using 0.866..., - * but leaving the other one here as a safety measure. - * Q->C_x = 0.2632401569273184856851; */ - Q->C_x = 0.8660254037844; - Q->C_y = 1.; - Q->A = 0.; - Q->B = 0.30396355092701331433; - - return setup(P); -} - - -PJ *PROJECTION(wag6) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->C_x = Q->C_y = 0.94745; - Q->A = 0.0; - Q->B = 0.30396355092701331433; - - return setup(P); -} - - -PJ *PROJECTION(putp1) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->C_x = 1.89490; - Q->C_y = 0.94745; - Q->A = -0.5; - Q->B = 0.30396355092701331433; - - return setup(P); -} diff --git a/src/PJ_eck3.cpp b/src/PJ_eck3.cpp new file mode 100644 index 00000000..7993d781 --- /dev/null +++ b/src/PJ_eck3.cpp @@ -0,0 +1,110 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(eck3, "Eckert III") "\n\tPCyl, Sph"; +PROJ_HEAD(putp1, "Putnins P1") "\n\tPCyl, Sph"; +PROJ_HEAD(wag6, "Wagner VI") "\n\tPCyl, Sph"; +PROJ_HEAD(kav7, "Kavraisky VII") "\n\tPCyl, Sph"; + +struct pj_opaque { + double C_x, C_y, A, B; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y = Q->C_y * lp.phi; + xy.x = Q->C_x * lp.lam * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi)); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double denominator; + + lp.phi = xy.y / Q->C_y; + denominator = (Q->C_x * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi))); + if ( denominator == 0.0) + lp.lam = HUGE_VAL; + else + lp.lam = xy.x / denominator; + return lp; +} + + +static PJ *setup(PJ *P) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(eck3) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->C_x = 0.42223820031577120149; + Q->C_y = 0.84447640063154240298; + Q->A = 1.0; + Q->B = 0.4052847345693510857755; + + return setup(P); +} + + +PJ *PROJECTION(kav7) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + /* Defined twice in original code - Using 0.866..., + * but leaving the other one here as a safety measure. + * Q->C_x = 0.2632401569273184856851; */ + Q->C_x = 0.8660254037844; + Q->C_y = 1.; + Q->A = 0.; + Q->B = 0.30396355092701331433; + + return setup(P); +} + + +PJ *PROJECTION(wag6) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->C_x = Q->C_y = 0.94745; + Q->A = 0.0; + Q->B = 0.30396355092701331433; + + return setup(P); +} + + +PJ *PROJECTION(putp1) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->C_x = 1.89490; + Q->C_y = 0.94745; + Q->A = -0.5; + Q->B = 0.30396355092701331433; + + return setup(P); +} diff --git a/src/PJ_eck4.c b/src/PJ_eck4.c deleted file mode 100644 index 4fa4c21f..00000000 --- a/src/PJ_eck4.c +++ /dev/null @@ -1,63 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph"; - -#define C_x .42223820031577120149 -#define C_y 1.32650042817700232218 -#define RC_y .75386330736002178205 -#define C_p 3.57079632679489661922 -#define RC_p .28004957675577868795 -#define EPS 1e-7 -#define NITER 6 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double p, V, s, c; - int i; - (void) P; - - p = C_p * sin(lp.phi); - V = lp.phi * lp.phi; - lp.phi *= 0.895168 + V * ( 0.0218849 + V * 0.00826809 ); - for (i = NITER; i ; --i) { - c = cos(lp.phi); - s = sin(lp.phi); - lp.phi -= V = (lp.phi + s * (c + 2.) - p) / - (1. + c * (c + 2.) - s * s); - if (fabs(V) < EPS) - break; - } - if (!i) { - xy.x = C_x * lp.lam; - xy.y = lp.phi < 0. ? -C_y : C_y; - } else { - xy.x = C_x * lp.lam * (1. + cos(lp.phi)); - xy.y = C_y * sin(lp.phi); - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double c; - - lp.phi = aasin(P->ctx,xy.y * RC_y); - lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi)))); - lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) * RC_p); - return lp; -} - - -PJ *PROJECTION(eck4) { - P->es = 0.0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_eck4.cpp b/src/PJ_eck4.cpp new file mode 100644 index 00000000..4fa4c21f --- /dev/null +++ b/src/PJ_eck4.cpp @@ -0,0 +1,63 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph"; + +#define C_x .42223820031577120149 +#define C_y 1.32650042817700232218 +#define RC_y .75386330736002178205 +#define C_p 3.57079632679489661922 +#define RC_p .28004957675577868795 +#define EPS 1e-7 +#define NITER 6 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double p, V, s, c; + int i; + (void) P; + + p = C_p * sin(lp.phi); + V = lp.phi * lp.phi; + lp.phi *= 0.895168 + V * ( 0.0218849 + V * 0.00826809 ); + for (i = NITER; i ; --i) { + c = cos(lp.phi); + s = sin(lp.phi); + lp.phi -= V = (lp.phi + s * (c + 2.) - p) / + (1. + c * (c + 2.) - s * s); + if (fabs(V) < EPS) + break; + } + if (!i) { + xy.x = C_x * lp.lam; + xy.y = lp.phi < 0. ? -C_y : C_y; + } else { + xy.x = C_x * lp.lam * (1. + cos(lp.phi)); + xy.y = C_y * sin(lp.phi); + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double c; + + lp.phi = aasin(P->ctx,xy.y * RC_y); + lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi)))); + lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) * RC_p); + return lp; +} + + +PJ *PROJECTION(eck4) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_eck5.c b/src/PJ_eck5.c deleted file mode 100644 index f9f28460..00000000 --- a/src/PJ_eck5.c +++ /dev/null @@ -1,40 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(eck5, "Eckert V") "\n\tPCyl, Sph"; - -#define XF 0.44101277172455148219 -#define RXF 2.26750802723822639137 -#define YF 0.88202554344910296438 -#define RYF 1.13375401361911319568 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - xy.x = XF * (1. + cos(lp.phi)) * lp.lam; - xy.y = YF * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - lp.lam = RXF * xy.x / (1. + cos( lp.phi = RYF * xy.y)); - - return lp; -} - - -PJ *PROJECTION(eck5) { - P->es = 0.0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_eck5.cpp b/src/PJ_eck5.cpp new file mode 100644 index 00000000..f9f28460 --- /dev/null +++ b/src/PJ_eck5.cpp @@ -0,0 +1,40 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(eck5, "Eckert V") "\n\tPCyl, Sph"; + +#define XF 0.44101277172455148219 +#define RXF 2.26750802723822639137 +#define YF 0.88202554344910296438 +#define RYF 1.13375401361911319568 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + xy.x = XF * (1. + cos(lp.phi)) * lp.lam; + xy.y = YF * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + lp.lam = RXF * xy.x / (1. + cos( lp.phi = RYF * xy.y)); + + return lp; +} + + +PJ *PROJECTION(eck5) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_eqc.c b/src/PJ_eqc.c deleted file mode 100644 index 07d6141c..00000000 --- a/src/PJ_eqc.c +++ /dev/null @@ -1,52 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -struct pj_opaque { - double rc; -}; - -PROJ_HEAD(eqc, "Equidistant Cylindrical (Plate Carree)") - "\n\tCyl, Sph\n\tlat_ts=[, lat_0=0]"; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.x = Q->rc * lp.lam; - xy.y = lp.phi - P->phi0; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - lp.lam = xy.x / Q->rc; - lp.phi = xy.y + P->phi0; - - return lp; -} - - -PJ *PROJECTION(eqc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if ((Q->rc = cos(pj_param(P->ctx, P->params, "rlat_ts").f)) <= 0.) - return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90); - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} diff --git a/src/PJ_eqc.cpp b/src/PJ_eqc.cpp new file mode 100644 index 00000000..b95471f7 --- /dev/null +++ b/src/PJ_eqc.cpp @@ -0,0 +1,52 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +struct pj_opaque { + double rc; +}; + +PROJ_HEAD(eqc, "Equidistant Cylindrical (Plate Carree)") + "\n\tCyl, Sph\n\tlat_ts=[, lat_0=0]"; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.x = Q->rc * lp.lam; + xy.y = lp.phi - P->phi0; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + lp.lam = xy.x / Q->rc; + lp.phi = xy.y + P->phi0; + + return lp; +} + + +PJ *PROJECTION(eqc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if ((Q->rc = cos(pj_param(P->ctx, P->params, "rlat_ts").f)) <= 0.) + return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90); + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} diff --git a/src/PJ_eqdc.c b/src/PJ_eqdc.c deleted file mode 100644 index 8caca87a..00000000 --- a/src/PJ_eqdc.c +++ /dev/null @@ -1,120 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -struct pj_opaque { - double phi1; - double phi2; - double n; - double rho; - double rho0; - double c; - double *en; - int ellips; -}; - -PROJ_HEAD(eqdc, "Equidistant Conic") - "\n\tConic, Sph&Ell\n\tlat_1= lat_2="; -# define EPS10 1.e-10 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - Q->rho = Q->c - (Q->ellips ? pj_mlfn(lp.phi, sin(lp.phi), - cos(lp.phi), Q->en) : lp.phi); - xy.x = Q->rho * sin( lp.lam *= Q->n ); - xy.y = Q->rho0 - Q->rho * cos(lp.lam); - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - if ((Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { - if (Q->n < 0.) { - Q->rho = -Q->rho; - xy.x = -xy.x; - xy.y = -xy.y; - } - lp.phi = Q->c - Q->rho; - if (Q->ellips) - lp.phi = pj_inv_mlfn(P->ctx, lp.phi, P->es, Q->en); - lp.lam = atan2(xy.x, xy.y) / Q->n; - } else { - lp.lam = 0.; - lp.phi = Q->n > 0. ? M_HALFPI : -M_HALFPI; - } - return lp; -} - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(eqdc) { - double cosphi, sinphi; - int secant; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - - if (fabs(Q->phi1 + Q->phi2) < EPS10) - return pj_default_destructor (P, PJD_ERR_CONIC_LAT_EQUAL); - - if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor(P, ENOMEM); - - Q->n = sinphi = sin(Q->phi1); - cosphi = cos(Q->phi1); - secant = fabs(Q->phi1 - Q->phi2) >= EPS10; - if( (Q->ellips = (P->es > 0.)) ) { - double ml1, m1; - - m1 = pj_msfn(sinphi, cosphi, P->es); - ml1 = pj_mlfn(Q->phi1, sinphi, cosphi, Q->en); - if (secant) { /* secant cone */ - sinphi = sin(Q->phi2); - cosphi = cos(Q->phi2); - Q->n = (m1 - pj_msfn(sinphi, cosphi, P->es)) / - (pj_mlfn(Q->phi2, sinphi, cosphi, Q->en) - ml1); - } - Q->c = ml1 + m1 / Q->n; - Q->rho0 = Q->c - pj_mlfn(P->phi0, sin(P->phi0), - cos(P->phi0), Q->en); - } else { - if (secant) - Q->n = (cosphi - cos(Q->phi2)) / (Q->phi2 - Q->phi1); - Q->c = Q->phi1 + cos(Q->phi1) / Q->n; - Q->rho0 = Q->c - P->phi0; - } - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_eqdc.cpp b/src/PJ_eqdc.cpp new file mode 100644 index 00000000..468b16df --- /dev/null +++ b/src/PJ_eqdc.cpp @@ -0,0 +1,120 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +struct pj_opaque { + double phi1; + double phi2; + double n; + double rho; + double rho0; + double c; + double *en; + int ellips; +}; + +PROJ_HEAD(eqdc, "Equidistant Conic") + "\n\tConic, Sph&Ell\n\tlat_1= lat_2="; +# define EPS10 1.e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + Q->rho = Q->c - (Q->ellips ? pj_mlfn(lp.phi, sin(lp.phi), + cos(lp.phi), Q->en) : lp.phi); + xy.x = Q->rho * sin( lp.lam *= Q->n ); + xy.y = Q->rho0 - Q->rho * cos(lp.lam); + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + if ((Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { + if (Q->n < 0.) { + Q->rho = -Q->rho; + xy.x = -xy.x; + xy.y = -xy.y; + } + lp.phi = Q->c - Q->rho; + if (Q->ellips) + lp.phi = pj_inv_mlfn(P->ctx, lp.phi, P->es, Q->en); + lp.lam = atan2(xy.x, xy.y) / Q->n; + } else { + lp.lam = 0.; + lp.phi = Q->n > 0. ? M_HALFPI : -M_HALFPI; + } + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(eqdc) { + double cosphi, sinphi; + int secant; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + + if (fabs(Q->phi1 + Q->phi2) < EPS10) + return pj_default_destructor (P, PJD_ERR_CONIC_LAT_EQUAL); + + if (!(Q->en = pj_enfn(P->es))) + return pj_default_destructor(P, ENOMEM); + + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if( (Q->ellips = (P->es > 0.)) ) { + double ml1, m1; + + m1 = pj_msfn(sinphi, cosphi, P->es); + ml1 = pj_mlfn(Q->phi1, sinphi, cosphi, Q->en); + if (secant) { /* secant cone */ + sinphi = sin(Q->phi2); + cosphi = cos(Q->phi2); + Q->n = (m1 - pj_msfn(sinphi, cosphi, P->es)) / + (pj_mlfn(Q->phi2, sinphi, cosphi, Q->en) - ml1); + } + Q->c = ml1 + m1 / Q->n; + Q->rho0 = Q->c - pj_mlfn(P->phi0, sin(P->phi0), + cos(P->phi0), Q->en); + } else { + if (secant) + Q->n = (cosphi - cos(Q->phi2)) / (Q->phi2 - Q->phi1); + Q->c = Q->phi1 + cos(Q->phi1) / Q->n; + Q->rho0 = Q->c - P->phi0; + } + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_eqearth.c b/src/PJ_eqearth.c deleted file mode 100644 index a0d4467b..00000000 --- a/src/PJ_eqearth.c +++ /dev/null @@ -1,162 +0,0 @@ -/* -Equal Earth is a projection inspired by the Robinson projection, but unlike -the Robinson projection retains the relative size of areas. The projection -was designed in 2018 by Bojan Savric, Tom Patterson and Bernhard Jenny. - -Publication: -Bojan Savric, Tom Patterson & Bernhard Jenny (2018). The Equal Earth map -projection, International Journal of Geographical Information Science, -DOI: 10.1080/13658816.2018.1504949 - -Port to PROJ by Juernjakob Dugge, 16 August 2018 -Added ellipsoidal equations by Bojan Savric, 22 August 2018 -*/ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(eqearth, "Equal Earth") "\n\tPCyl, Sph&Ell"; - -/* A1..A4, polynomial coefficients */ -#define A1 1.340264 -#define A2 -0.081106 -#define A3 0.000893 -#define A4 0.003796 -#define M (sqrt(3) / 2.0) - -#define MAX_Y 1.3173627591574 /* 90° latitude on a sphere with radius 1 */ -#define EPS 1e-11 -#define MAX_ITER 12 - -struct pj_opaque { - double qp; - double rqda; - double *apa; -}; - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal/spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sbeta; - double psi, psi2, psi6; - - /* Spheroidal case, using sine latitude */ - sbeta = sin(lp.phi); - - /* In the ellipsoidal case, we convert sbeta to sine of authalic latitude */ - if (P->es != 0.0) { - sbeta = pj_qsfn(sbeta, P->e, 1.0 - P->es) / Q->qp; - - /* Rounding error. */ - if (fabs(sbeta) > 1) - sbeta = sbeta > 0 ? 1 : -1; - } - - /* Equal Earth projection */ - psi = asin(M * sbeta); - psi2 = psi * psi; - psi6 = psi2 * psi2 * psi2; - - xy.x = lp.lam * cos(psi) / (M * (A1 + 3 * A2 * psi2 + psi6 * (7 * A3 + 9 * A4 * psi2))); - xy.y = psi * (A1 + A2 * psi2 + psi6 * (A3 + A4 * psi2)); - - /* Adjusting x and y for authalic radius */ - xy.x *= Q->rqda; - xy.y *= Q->rqda; - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal/spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double yc, y2, y6; - int i; - - /* Adjusting x and y for authalic radius */ - xy.x /= Q->rqda; - xy.y /= Q->rqda; - - /* Make sure y is inside valid range */ - if (xy.y > MAX_Y) - xy.y = MAX_Y; - else if (xy.y < -MAX_Y) - xy.y = -MAX_Y; - - yc = xy.y; - - /* Newton-Raphson */ - for (i = MAX_ITER; i ; --i) { - double f, fder, tol; - - y2 = yc * yc; - y6 = y2 * y2 * y2; - - f = yc * (A1 + A2 * y2 + y6 * (A3 + A4 * y2)) - xy.y; - fder = A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2); - - tol = f / fder; - yc -= tol; - - if (fabs(tol) < EPS) - break; - } - - if( i == 0 ) { - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - return lp; - } - - /* Longitude */ - y2 = yc * yc; - y6 = y2 * y2 * y2; - - lp.lam = M * xy.x * (A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2)) / cos(yc); - - /* Latitude (for spheroidal case, this is latitude */ - lp.phi = asin(sin(yc) / M); - - /* Ellipsoidal case, converting auth. latitude */ - if (P->es != 0.0) - lp.phi = pj_authlat(lp.phi, Q->apa); - - return lp; -} - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->apa); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(eqearth) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - P->fwd = e_forward; - P->inv = e_inverse; - Q->rqda = 1.0; - - /* Ellipsoidal case */ - if (P->es != 0.0) { - Q->apa = pj_authset(P->es); /* For auth_lat(). */ - if (0 == Q->apa) - return destructor(P, ENOMEM); - Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ - Q->rqda = sqrt(0.5*Q->qp); /* Authalic radius divided by major axis */ - } - - return P; -} diff --git a/src/PJ_eqearth.cpp b/src/PJ_eqearth.cpp new file mode 100644 index 00000000..34d85fa5 --- /dev/null +++ b/src/PJ_eqearth.cpp @@ -0,0 +1,162 @@ +/* +Equal Earth is a projection inspired by the Robinson projection, but unlike +the Robinson projection retains the relative size of areas. The projection +was designed in 2018 by Bojan Savric, Tom Patterson and Bernhard Jenny. + +Publication: +Bojan Savric, Tom Patterson & Bernhard Jenny (2018). The Equal Earth map +projection, International Journal of Geographical Information Science, +DOI: 10.1080/13658816.2018.1504949 + +Port to PROJ by Juernjakob Dugge, 16 August 2018 +Added ellipsoidal equations by Bojan Savric, 22 August 2018 +*/ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(eqearth, "Equal Earth") "\n\tPCyl, Sph&Ell"; + +/* A1..A4, polynomial coefficients */ +#define A1 1.340264 +#define A2 -0.081106 +#define A3 0.000893 +#define A4 0.003796 +#define M (sqrt(3) / 2.0) + +#define MAX_Y 1.3173627591574 /* 90° latitude on a sphere with radius 1 */ +#define EPS 1e-11 +#define MAX_ITER 12 + +struct pj_opaque { + double qp; + double rqda; + double *apa; +}; + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal/spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sbeta; + double psi, psi2, psi6; + + /* Spheroidal case, using sine latitude */ + sbeta = sin(lp.phi); + + /* In the ellipsoidal case, we convert sbeta to sine of authalic latitude */ + if (P->es != 0.0) { + sbeta = pj_qsfn(sbeta, P->e, 1.0 - P->es) / Q->qp; + + /* Rounding error. */ + if (fabs(sbeta) > 1) + sbeta = sbeta > 0 ? 1 : -1; + } + + /* Equal Earth projection */ + psi = asin(M * sbeta); + psi2 = psi * psi; + psi6 = psi2 * psi2 * psi2; + + xy.x = lp.lam * cos(psi) / (M * (A1 + 3 * A2 * psi2 + psi6 * (7 * A3 + 9 * A4 * psi2))); + xy.y = psi * (A1 + A2 * psi2 + psi6 * (A3 + A4 * psi2)); + + /* Adjusting x and y for authalic radius */ + xy.x *= Q->rqda; + xy.y *= Q->rqda; + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal/spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double yc, y2, y6; + int i; + + /* Adjusting x and y for authalic radius */ + xy.x /= Q->rqda; + xy.y /= Q->rqda; + + /* Make sure y is inside valid range */ + if (xy.y > MAX_Y) + xy.y = MAX_Y; + else if (xy.y < -MAX_Y) + xy.y = -MAX_Y; + + yc = xy.y; + + /* Newton-Raphson */ + for (i = MAX_ITER; i ; --i) { + double f, fder, tol; + + y2 = yc * yc; + y6 = y2 * y2 * y2; + + f = yc * (A1 + A2 * y2 + y6 * (A3 + A4 * y2)) - xy.y; + fder = A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2); + + tol = f / fder; + yc -= tol; + + if (fabs(tol) < EPS) + break; + } + + if( i == 0 ) { + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + return lp; + } + + /* Longitude */ + y2 = yc * yc; + y6 = y2 * y2 * y2; + + lp.lam = M * xy.x * (A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2)) / cos(yc); + + /* Latitude (for spheroidal case, this is latitude */ + lp.phi = asin(sin(yc) / M); + + /* Ellipsoidal case, converting auth. latitude */ + if (P->es != 0.0) + lp.phi = pj_authlat(lp.phi, Q->apa); + + return lp; +} + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->apa); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(eqearth) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + P->fwd = e_forward; + P->inv = e_inverse; + Q->rqda = 1.0; + + /* Ellipsoidal case */ + if (P->es != 0.0) { + Q->apa = pj_authset(P->es); /* For auth_lat(). */ + if (0 == Q->apa) + return destructor(P, ENOMEM); + Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + Q->rqda = sqrt(0.5*Q->qp); /* Authalic radius divided by major axis */ + } + + return P; +} diff --git a/src/PJ_fahey.c b/src/PJ_fahey.c deleted file mode 100644 index 85e0ab69..00000000 --- a/src/PJ_fahey.c +++ /dev/null @@ -1,41 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph"; - -#define TOL 1e-6 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = tan(0.5 * lp.phi); - xy.y = 1.819152 * xy.x; - xy.x = 0.819152 * lp.lam * asqrt(1 - xy.x * xy.x); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - - xy.y /= 1.819152; - lp.phi = 2. * atan(xy.y); - xy.y = 1. - xy.y * xy.y; - lp.lam = fabs(xy.y) < TOL ? 0. : xy.x / (0.819152 * sqrt(xy.y)); - return lp; -} - - -PJ *PROJECTION(fahey) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_fahey.cpp b/src/PJ_fahey.cpp new file mode 100644 index 00000000..85e0ab69 --- /dev/null +++ b/src/PJ_fahey.cpp @@ -0,0 +1,41 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph"; + +#define TOL 1e-6 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = tan(0.5 * lp.phi); + xy.y = 1.819152 * xy.x; + xy.x = 0.819152 * lp.lam * asqrt(1 - xy.x * xy.x); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + xy.y /= 1.819152; + lp.phi = 2. * atan(xy.y); + xy.y = 1. - xy.y * xy.y; + lp.lam = fabs(xy.y) < TOL ? 0. : xy.x / (0.819152 * sqrt(xy.y)); + return lp; +} + + +PJ *PROJECTION(fahey) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_fouc_s.c b/src/PJ_fouc_s.c deleted file mode 100644 index 6efe34bd..00000000 --- a/src/PJ_fouc_s.c +++ /dev/null @@ -1,70 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(fouc_s, "Foucaut Sinusoidal") "\n\tPCyl, Sph"; - -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 - -struct pj_opaque { - double n, n1; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double t; - - t = cos(lp.phi); - xy.x = lp.lam * t / (Q->n + Q->n1 * t); - xy.y = Q->n * lp.phi + Q->n1 * sin(lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double V; - int i; - - if (Q->n != 0.0) { - lp.phi = xy.y; - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (Q->n * lp.phi + Q->n1 * sin(lp.phi) - xy.y ) / - (Q->n + Q->n1 * cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) - lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; - } else - lp.phi = aasin(P->ctx,xy.y); - V = cos(lp.phi); - lp.lam = xy.x * (Q->n + Q->n1 * V) / V; - return lp; -} - - -PJ *PROJECTION(fouc_s) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = pj_param(P->ctx, P->params, "dn").f; - if (Q->n < 0. || Q->n > 1.) - return pj_default_destructor (P, PJD_ERR_N_OUT_OF_RANGE); - - Q->n1 = 1. - Q->n; - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_fouc_s.cpp b/src/PJ_fouc_s.cpp new file mode 100644 index 00000000..8c223336 --- /dev/null +++ b/src/PJ_fouc_s.cpp @@ -0,0 +1,70 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(fouc_s, "Foucaut Sinusoidal") "\n\tPCyl, Sph"; + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + +struct pj_opaque { + double n, n1; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t; + + t = cos(lp.phi); + xy.x = lp.lam * t / (Q->n + Q->n1 * t); + xy.y = Q->n * lp.phi + Q->n1 * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double V; + int i; + + if (Q->n != 0.0) { + lp.phi = xy.y; + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (Q->n * lp.phi + Q->n1 * sin(lp.phi) - xy.y ) / + (Q->n + Q->n1 * cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) + lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; + } else + lp.phi = aasin(P->ctx,xy.y); + V = cos(lp.phi); + lp.lam = xy.x * (Q->n + Q->n1 * V) / V; + return lp; +} + + +PJ *PROJECTION(fouc_s) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = pj_param(P->ctx, P->params, "dn").f; + if (Q->n < 0. || Q->n > 1.) + return pj_default_destructor (P, PJD_ERR_N_OUT_OF_RANGE); + + Q->n1 = 1. - Q->n; + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_gall.c b/src/PJ_gall.c deleted file mode 100644 index a8697482..00000000 --- a/src/PJ_gall.c +++ /dev/null @@ -1,44 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(gall, "Gall (Gall Stereographic)") "\n\tCyl, Sph"; - -#define YF 1.70710678118654752440 -#define XF 0.70710678118654752440 -#define RYF 0.58578643762690495119 -#define RXF 1.41421356237309504880 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = XF * lp.lam; - xy.y = YF * tan(.5 * lp.phi); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - - lp.lam = RXF * xy.x; - lp.phi = 2. * atan(xy.y * RYF); - - return lp; -} - - -PJ *PROJECTION(gall) { - P->es = 0.0; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_gall.cpp b/src/PJ_gall.cpp new file mode 100644 index 00000000..a8697482 --- /dev/null +++ b/src/PJ_gall.cpp @@ -0,0 +1,44 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(gall, "Gall (Gall Stereographic)") "\n\tCyl, Sph"; + +#define YF 1.70710678118654752440 +#define XF 0.70710678118654752440 +#define RYF 0.58578643762690495119 +#define RXF 1.41421356237309504880 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = XF * lp.lam; + xy.y = YF * tan(.5 * lp.phi); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = RXF * xy.x; + lp.phi = 2. * atan(xy.y * RYF); + + return lp; +} + + +PJ *PROJECTION(gall) { + P->es = 0.0; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_geoc.c b/src/PJ_geoc.c deleted file mode 100644 index 0455fada..00000000 --- a/src/PJ_geoc.c +++ /dev/null @@ -1,59 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Conversion from geographic to geocentric latitude and back. - * Author: Thomas Knudsen (2017) - * - ****************************************************************************** - * Copyright (c) 2017, SDFE, http://www.sdfe.dk - * Copyright (c) 2017, Thomas Knudsen - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(geoc, "Geocentric Latitude"); - -/* Geographical to geocentric */ -static PJ_COORD forward(PJ_COORD coo, PJ *P) { - return pj_geocentric_latitude (P, PJ_FWD, coo); -} - -/* Geocentric to geographical */ -static PJ_COORD inverse(PJ_COORD coo, PJ *P) { - return pj_geocentric_latitude (P, PJ_INV, coo); -} - - -static PJ *CONVERSION(geoc, 1) { - P->inv4d = inverse; - P->fwd4d = forward; - - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - - P->is_latlong = 1; - return P; -} diff --git a/src/PJ_geoc.cpp b/src/PJ_geoc.cpp new file mode 100644 index 00000000..0455fada --- /dev/null +++ b/src/PJ_geoc.cpp @@ -0,0 +1,59 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Conversion from geographic to geocentric latitude and back. + * Author: Thomas Knudsen (2017) + * + ****************************************************************************** + * Copyright (c) 2017, SDFE, http://www.sdfe.dk + * Copyright (c) 2017, Thomas Knudsen + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(geoc, "Geocentric Latitude"); + +/* Geographical to geocentric */ +static PJ_COORD forward(PJ_COORD coo, PJ *P) { + return pj_geocentric_latitude (P, PJ_FWD, coo); +} + +/* Geocentric to geographical */ +static PJ_COORD inverse(PJ_COORD coo, PJ *P) { + return pj_geocentric_latitude (P, PJ_INV, coo); +} + + +static PJ *CONVERSION(geoc, 1) { + P->inv4d = inverse; + P->fwd4d = forward; + + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + + P->is_latlong = 1; + return P; +} diff --git a/src/PJ_geos.c b/src/PJ_geos.c deleted file mode 100644 index 0eb25610..00000000 --- a/src/PJ_geos.c +++ /dev/null @@ -1,236 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2004 Gerald I. Evenden -** Copyright (c) 2012 Martin Raspaud -** -** See also (section 4.4.3.2): -** http://www.eumetsat.int/en/area4/msg/news/us_doc/cgms_03_26.pdf -** -** 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. -*/ - -#define PJ_LIB__ -#include -#include -#include - -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -struct pj_opaque { - double h; - double radius_p; - double radius_p2; - double radius_p_inv2; - double radius_g; - double radius_g_1; - double C; - int flip_axis; -}; - -PROJ_HEAD(geos, "Geostationary Satellite View") "\n\tAzi, Sph&Ell\n\th="; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double Vx, Vy, Vz, tmp; - - /* Calculation of the three components of the vector from satellite to - ** position on earth surface (lon,lat).*/ - tmp = cos(lp.phi); - Vx = cos (lp.lam) * tmp; - Vy = sin (lp.lam) * tmp; - Vz = sin (lp.phi); - - /* Check visibility*/ - - - /* Calculation based on view angles from satellite.*/ - tmp = Q->radius_g - Vx; - - if(Q->flip_axis) { - xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp)); - xy.y = Q->radius_g_1 * atan(Vz / tmp); - } else { - xy.x = Q->radius_g_1 * atan(Vy / tmp); - xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp)); - } - - return xy; -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double r, Vx, Vy, Vz, tmp; - - /* Calculation of geocentric latitude. */ - lp.phi = atan (Q->radius_p2 * tan (lp.phi)); - - /* Calculation of the three components of the vector from satellite to - ** position on earth surface (lon,lat).*/ - r = (Q->radius_p) / hypot(Q->radius_p * cos (lp.phi), sin (lp.phi)); - Vx = r * cos (lp.lam) * cos (lp.phi); - Vy = r * sin (lp.lam) * cos (lp.phi); - Vz = r * sin (lp.phi); - - /* Check visibility. */ - if (((Q->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * Q->radius_p_inv2) < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - /* Calculation based on view angles from satellite. */ - tmp = Q->radius_g - Vx; - - if(Q->flip_axis) { - xy.x = Q->radius_g_1 * atan (Vy / hypot (Vz, tmp)); - xy.y = Q->radius_g_1 * atan (Vz / tmp); - } else { - xy.x = Q->radius_g_1 * atan (Vy / tmp); - xy.y = Q->radius_g_1 * atan (Vz / hypot (Vy, tmp)); - } - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double Vx, Vy, Vz, a, b, det, k; - - /* Setting three components of vector from satellite to position.*/ - Vx = -1.0; - if(Q->flip_axis) { - Vz = tan (xy.y / (Q->radius_g - 1.0)); - Vy = tan (xy.x / (Q->radius_g - 1.0)) * sqrt (1.0 + Vz * Vz); - } else { - Vy = tan (xy.x / (Q->radius_g - 1.0)); - Vz = tan (xy.y / (Q->radius_g - 1.0)) * sqrt (1.0 + Vy * Vy); - } - - /* Calculation of terms in cubic equation and determinant.*/ - a = Vy * Vy + Vz * Vz + Vx * Vx; - b = 2 * Q->radius_g * Vx; - if ((det = (b * b) - 4 * a * Q->C) < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - - /* Calculation of three components of vector from satellite to position.*/ - k = (-b - sqrt(det)) / (2 * a); - Vx = Q->radius_g + k * Vx; - Vy *= k; - Vz *= k; - - /* Calculation of longitude and latitude.*/ - lp.lam = atan2 (Vy, Vx); - lp.phi = atan (Vz * cos (lp.lam) / Vx); - - return lp; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double Vx, Vy, Vz, a, b, det, k; - - /* Setting three components of vector from satellite to position.*/ - Vx = -1.0; - - if(Q->flip_axis) { - Vz = tan (xy.y / Q->radius_g_1); - Vy = tan (xy.x / Q->radius_g_1) * hypot(1.0, Vz); - } else { - Vy = tan (xy.x / Q->radius_g_1); - Vz = tan (xy.y / Q->radius_g_1) * hypot(1.0, Vy); - } - - /* Calculation of terms in cubic equation and determinant.*/ - a = Vz / Q->radius_p; - a = Vy * Vy + a * a + Vx * Vx; - b = 2 * Q->radius_g * Vx; - if ((det = (b * b) - 4 * a * Q->C) < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - - /* Calculation of three components of vector from satellite to position.*/ - k = (-b - sqrt(det)) / (2. * a); - Vx = Q->radius_g + k * Vx; - Vy *= k; - Vz *= k; - - /* Calculation of longitude and latitude.*/ - lp.lam = atan2 (Vy, Vx); - lp.phi = atan (Vz * cos (lp.lam) / Vx); - lp.phi = atan (Q->radius_p_inv2 * tan (lp.phi)); - - return lp; -} - - -PJ *PROJECTION(geos) { - char *sweep_axis; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if ((Q->h = pj_param(P->ctx, P->params, "dh").f) <= 0.) - return pj_default_destructor (P, PJD_ERR_H_LESS_THAN_ZERO); - - sweep_axis = pj_param(P->ctx, P->params, "ssweep").s; - if (sweep_axis == NULL) - Q->flip_axis = 0; - else { - if ((sweep_axis[0] != 'x' && sweep_axis[0] != 'y') || - sweep_axis[1] != '\0') - return pj_default_destructor (P, PJD_ERR_INVALID_SWEEP_AXIS); - - if (sweep_axis[0] == 'x') - Q->flip_axis = 1; - else - Q->flip_axis = 0; - } - - Q->radius_g_1 = Q->h / P->a; - Q->radius_g = 1. + Q->radius_g_1; - Q->C = Q->radius_g * Q->radius_g - 1.0; - if (P->es != 0.0) { - Q->radius_p = sqrt (P->one_es); - Q->radius_p2 = P->one_es; - Q->radius_p_inv2 = P->rone_es; - P->inv = e_inverse; - P->fwd = e_forward; - } else { - Q->radius_p = Q->radius_p2 = Q->radius_p_inv2 = 1.0; - P->inv = s_inverse; - P->fwd = s_forward; - } - - return P; -} diff --git a/src/PJ_geos.cpp b/src/PJ_geos.cpp new file mode 100644 index 00000000..ffe0771c --- /dev/null +++ b/src/PJ_geos.cpp @@ -0,0 +1,236 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2004 Gerald I. Evenden +** Copyright (c) 2012 Martin Raspaud +** +** See also (section 4.4.3.2): +** http://www.eumetsat.int/en/area4/msg/news/us_doc/cgms_03_26.pdf +** +** 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. +*/ + +#define PJ_LIB__ +#include +#include +#include + +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +struct pj_opaque { + double h; + double radius_p; + double radius_p2; + double radius_p_inv2; + double radius_g; + double radius_g_1; + double C; + int flip_axis; +}; + +PROJ_HEAD(geos, "Geostationary Satellite View") "\n\tAzi, Sph&Ell\n\th="; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double Vx, Vy, Vz, tmp; + + /* Calculation of the three components of the vector from satellite to + ** position on earth surface (lon,lat).*/ + tmp = cos(lp.phi); + Vx = cos (lp.lam) * tmp; + Vy = sin (lp.lam) * tmp; + Vz = sin (lp.phi); + + /* Check visibility*/ + + + /* Calculation based on view angles from satellite.*/ + tmp = Q->radius_g - Vx; + + if(Q->flip_axis) { + xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp)); + xy.y = Q->radius_g_1 * atan(Vz / tmp); + } else { + xy.x = Q->radius_g_1 * atan(Vy / tmp); + xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp)); + } + + return xy; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double r, Vx, Vy, Vz, tmp; + + /* Calculation of geocentric latitude. */ + lp.phi = atan (Q->radius_p2 * tan (lp.phi)); + + /* Calculation of the three components of the vector from satellite to + ** position on earth surface (lon,lat).*/ + r = (Q->radius_p) / hypot(Q->radius_p * cos (lp.phi), sin (lp.phi)); + Vx = r * cos (lp.lam) * cos (lp.phi); + Vy = r * sin (lp.lam) * cos (lp.phi); + Vz = r * sin (lp.phi); + + /* Check visibility. */ + if (((Q->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * Q->radius_p_inv2) < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + /* Calculation based on view angles from satellite. */ + tmp = Q->radius_g - Vx; + + if(Q->flip_axis) { + xy.x = Q->radius_g_1 * atan (Vy / hypot (Vz, tmp)); + xy.y = Q->radius_g_1 * atan (Vz / tmp); + } else { + xy.x = Q->radius_g_1 * atan (Vy / tmp); + xy.y = Q->radius_g_1 * atan (Vz / hypot (Vy, tmp)); + } + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double Vx, Vy, Vz, a, b, det, k; + + /* Setting three components of vector from satellite to position.*/ + Vx = -1.0; + if(Q->flip_axis) { + Vz = tan (xy.y / (Q->radius_g - 1.0)); + Vy = tan (xy.x / (Q->radius_g - 1.0)) * sqrt (1.0 + Vz * Vz); + } else { + Vy = tan (xy.x / (Q->radius_g - 1.0)); + Vz = tan (xy.y / (Q->radius_g - 1.0)) * sqrt (1.0 + Vy * Vy); + } + + /* Calculation of terms in cubic equation and determinant.*/ + a = Vy * Vy + Vz * Vz + Vx * Vx; + b = 2 * Q->radius_g * Vx; + if ((det = (b * b) - 4 * a * Q->C) < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + + /* Calculation of three components of vector from satellite to position.*/ + k = (-b - sqrt(det)) / (2 * a); + Vx = Q->radius_g + k * Vx; + Vy *= k; + Vz *= k; + + /* Calculation of longitude and latitude.*/ + lp.lam = atan2 (Vy, Vx); + lp.phi = atan (Vz * cos (lp.lam) / Vx); + + return lp; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double Vx, Vy, Vz, a, b, det, k; + + /* Setting three components of vector from satellite to position.*/ + Vx = -1.0; + + if(Q->flip_axis) { + Vz = tan (xy.y / Q->radius_g_1); + Vy = tan (xy.x / Q->radius_g_1) * hypot(1.0, Vz); + } else { + Vy = tan (xy.x / Q->radius_g_1); + Vz = tan (xy.y / Q->radius_g_1) * hypot(1.0, Vy); + } + + /* Calculation of terms in cubic equation and determinant.*/ + a = Vz / Q->radius_p; + a = Vy * Vy + a * a + Vx * Vx; + b = 2 * Q->radius_g * Vx; + if ((det = (b * b) - 4 * a * Q->C) < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + + /* Calculation of three components of vector from satellite to position.*/ + k = (-b - sqrt(det)) / (2. * a); + Vx = Q->radius_g + k * Vx; + Vy *= k; + Vz *= k; + + /* Calculation of longitude and latitude.*/ + lp.lam = atan2 (Vy, Vx); + lp.phi = atan (Vz * cos (lp.lam) / Vx); + lp.phi = atan (Q->radius_p_inv2 * tan (lp.phi)); + + return lp; +} + + +PJ *PROJECTION(geos) { + char *sweep_axis; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if ((Q->h = pj_param(P->ctx, P->params, "dh").f) <= 0.) + return pj_default_destructor (P, PJD_ERR_H_LESS_THAN_ZERO); + + sweep_axis = pj_param(P->ctx, P->params, "ssweep").s; + if (sweep_axis == NULL) + Q->flip_axis = 0; + else { + if ((sweep_axis[0] != 'x' && sweep_axis[0] != 'y') || + sweep_axis[1] != '\0') + return pj_default_destructor (P, PJD_ERR_INVALID_SWEEP_AXIS); + + if (sweep_axis[0] == 'x') + Q->flip_axis = 1; + else + Q->flip_axis = 0; + } + + Q->radius_g_1 = Q->h / P->a; + Q->radius_g = 1. + Q->radius_g_1; + Q->C = Q->radius_g * Q->radius_g - 1.0; + if (P->es != 0.0) { + Q->radius_p = sqrt (P->one_es); + Q->radius_p2 = P->one_es; + Q->radius_p_inv2 = P->rone_es; + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->radius_p = Q->radius_p2 = Q->radius_p_inv2 = 1.0; + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} diff --git a/src/PJ_gins8.c b/src/PJ_gins8.c deleted file mode 100644 index c1a2fff0..00000000 --- a/src/PJ_gins8.c +++ /dev/null @@ -1,33 +0,0 @@ -#define PJ_LIB__ -#include "projects.h" - -PROJ_HEAD(gins8, "Ginsburg VIII (TsNIIGAiK)") "\n\tPCyl, Sph, no inv"; - -#define Cl 0.000952426 -#define Cp 0.162388 -#define C12 0.08333333333333333 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double t = lp.phi * lp.phi; - (void) P; - - xy.y = lp.phi * (1. + t * C12); - xy.x = lp.lam * (1. - Cp * t); - t = lp.lam * lp.lam; - xy.x *= (0.87 - Cl * t * t); - - return xy; -} - - -PJ *PROJECTION(gins8) { - P->es = 0.0; - P->inv = 0; - P->fwd = s_forward; - - return P; -} - - diff --git a/src/PJ_gins8.cpp b/src/PJ_gins8.cpp new file mode 100644 index 00000000..c1a2fff0 --- /dev/null +++ b/src/PJ_gins8.cpp @@ -0,0 +1,33 @@ +#define PJ_LIB__ +#include "projects.h" + +PROJ_HEAD(gins8, "Ginsburg VIII (TsNIIGAiK)") "\n\tPCyl, Sph, no inv"; + +#define Cl 0.000952426 +#define Cp 0.162388 +#define C12 0.08333333333333333 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double t = lp.phi * lp.phi; + (void) P; + + xy.y = lp.phi * (1. + t * C12); + xy.x = lp.lam * (1. - Cp * t); + t = lp.lam * lp.lam; + xy.x *= (0.87 - Cl * t * t); + + return xy; +} + + +PJ *PROJECTION(gins8) { + P->es = 0.0; + P->inv = 0; + P->fwd = s_forward; + + return P; +} + + diff --git a/src/PJ_gn_sinu.c b/src/PJ_gn_sinu.c deleted file mode 100644 index 2ef0f263..00000000 --- a/src/PJ_gn_sinu.c +++ /dev/null @@ -1,187 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph\n\tm= n="; -PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell"; -PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph"; -PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph"; - -#define EPS10 1e-10 -#define MAX_ITER 8 -#define LOOP_TOL 1e-7 - -struct pj_opaque { - double *en; - double m, n, C_x, C_y; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - double s, c; - - xy.y = pj_mlfn(lp.phi, s = sin(lp.phi), c = cos(lp.phi), P->opaque->en); - xy.x = lp.lam * c / sqrt(1. - P->es * s * s); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - double s; - - if ((s = fabs(lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, P->opaque->en))) < M_HALFPI) { - s = sin(lp.phi); - lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi); - } else if ((s - EPS10) < M_HALFPI) { - lp.lam = 0.; - } else { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - } - - return lp; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - if (Q->m == 0.0) - lp.phi = Q->n != 1. ? aasin(P->ctx,Q->n * sin(lp.phi)): lp.phi; - else { - double k, V; - int i; - - k = Q->n * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (Q->m * lp.phi + sin(lp.phi) - k) / - (Q->m + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - } - xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi)); - xy.y = Q->C_y * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y /= Q->C_y; - lp.phi = (Q->m != 0.0) ? aasin(P->ctx,(Q->m * xy.y + sin(xy.y)) / Q->n) : - ( Q->n != 1. ? aasin(P->ctx,sin(xy.y) / Q->n) : xy.y ); - lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y))); - return lp; -} - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - - -/* for spheres, only */ -static void setup(PJ *P) { - struct pj_opaque *Q = P->opaque; - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - - Q->C_x = (Q->C_y = sqrt((Q->m + 1.) / Q->n))/(Q->m + 1.); -} - - -PJ *PROJECTION(sinu) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor (P, ENOMEM); - - if (P->es != 0.0) { - P->inv = e_inverse; - P->fwd = e_forward; - } else { - Q->n = 1.; - Q->m = 0.; - setup(P); - } - return P; -} - - -PJ *PROJECTION(eck6) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->m = 1.; - Q->n = 2.570796326794896619231321691; - setup(P); - - return P; -} - - -PJ *PROJECTION(mbtfps) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->m = 0.5; - Q->n = 1.785398163397448309615660845; - setup(P); - - return P; -} - - -PJ *PROJECTION(gn_sinu) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) { - Q->n = pj_param(P->ctx, P->params, "dn").f; - Q->m = pj_param(P->ctx, P->params, "dm").f; - if (Q->n <= 0 || Q->m < 0) - return destructor (P, PJD_ERR_INVALID_M_OR_N); - } else - return destructor (P, PJD_ERR_INVALID_M_OR_N); - - setup(P); - - return P; -} diff --git a/src/PJ_gn_sinu.cpp b/src/PJ_gn_sinu.cpp new file mode 100644 index 00000000..2c7824ac --- /dev/null +++ b/src/PJ_gn_sinu.cpp @@ -0,0 +1,187 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph\n\tm= n="; +PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell"; +PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph"; +PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph"; + +#define EPS10 1e-10 +#define MAX_ITER 8 +#define LOOP_TOL 1e-7 + +struct pj_opaque { + double *en; + double m, n, C_x, C_y; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + double s, c; + + xy.y = pj_mlfn(lp.phi, s = sin(lp.phi), c = cos(lp.phi), static_cast(P->opaque)->en); + xy.x = lp.lam * c / sqrt(1. - P->es * s * s); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + double s; + + if ((s = fabs(lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, static_cast(P->opaque)->en))) < M_HALFPI) { + s = sin(lp.phi); + lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi); + } else if ((s - EPS10) < M_HALFPI) { + lp.lam = 0.; + } else { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + } + + return lp; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + if (Q->m == 0.0) + lp.phi = Q->n != 1. ? aasin(P->ctx,Q->n * sin(lp.phi)): lp.phi; + else { + double k, V; + int i; + + k = Q->n * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (Q->m * lp.phi + sin(lp.phi) - k) / + (Q->m + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + } + xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi)); + xy.y = Q->C_y * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y /= Q->C_y; + lp.phi = (Q->m != 0.0) ? aasin(P->ctx,(Q->m * xy.y + sin(xy.y)) / Q->n) : + ( Q->n != 1. ? aasin(P->ctx,sin(xy.y) / Q->n) : xy.y ); + lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y))); + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + + +/* for spheres, only */ +static void setup(PJ *P) { + struct pj_opaque *Q = static_cast(P->opaque); + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + Q->C_x = (Q->C_y = sqrt((Q->m + 1.) / Q->n))/(Q->m + 1.); +} + + +PJ *PROJECTION(sinu) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + if (!(Q->en = pj_enfn(P->es))) + return pj_default_destructor (P, ENOMEM); + + if (P->es != 0.0) { + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->n = 1.; + Q->m = 0.; + setup(P); + } + return P; +} + + +PJ *PROJECTION(eck6) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->m = 1.; + Q->n = 2.570796326794896619231321691; + setup(P); + + return P; +} + + +PJ *PROJECTION(mbtfps) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->m = 0.5; + Q->n = 1.785398163397448309615660845; + setup(P); + + return P; +} + + +PJ *PROJECTION(gn_sinu) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) { + Q->n = pj_param(P->ctx, P->params, "dn").f; + Q->m = pj_param(P->ctx, P->params, "dm").f; + if (Q->n <= 0 || Q->m < 0) + return destructor (P, PJD_ERR_INVALID_M_OR_N); + } else + return destructor (P, PJD_ERR_INVALID_M_OR_N); + + setup(P); + + return P; +} diff --git a/src/PJ_gnom.c b/src/PJ_gnom.c deleted file mode 100644 index 635ae49e..00000000 --- a/src/PJ_gnom.c +++ /dev/null @@ -1,143 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(gnom, "Gnomonic") "\n\tAzi, Sph"; - -#define EPS10 1.e-10 - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double sinph0; - double cosph0; - enum Mode mode; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - - switch (Q->mode) { - case EQUIT: - xy.y = cosphi * coslam; - break; - case OBLIQ: - xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; - break; - case S_POLE: - xy.y = - sinphi; - break; - case N_POLE: - xy.y = sinphi; - break; - } - - if (xy.y <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - xy.x = (xy.y = 1. / xy.y) * cosphi * sin(lp.lam); - switch (Q->mode) { - case EQUIT: - xy.y *= sinphi; - break; - case OBLIQ: - xy.y *= Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = - coslam; - /*-fallthrough*/ - case S_POLE: - xy.y *= cosphi * coslam; - break; - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double rh, cosz, sinz; - - rh = hypot(xy.x, xy.y); - sinz = sin(lp.phi = atan(rh)); - cosz = sqrt(1. - sinz * sinz); - - if (fabs(rh) <= EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - } else { - switch (Q->mode) { - case OBLIQ: - lp.phi = cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh; - if (fabs(lp.phi) >= 1.) - lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; - else - lp.phi = asin(lp.phi); - xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; - xy.x *= sinz * Q->cosph0; - break; - case EQUIT: - lp.phi = xy.y * sinz / rh; - if (fabs(lp.phi) >= 1.) - lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; - else - lp.phi = asin(lp.phi); - xy.y = cosz * rh; - xy.x *= sinz; - break; - case S_POLE: - lp.phi -= M_HALFPI; - break; - case N_POLE: - lp.phi = M_HALFPI - lp.phi; - xy.y = -xy.y; - break; - } - lp.lam = atan2(xy.x, xy.y); - } - return lp; -} - - -PJ *PROJECTION(gnom) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) { - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - } else if (fabs(P->phi0) < EPS10) { - Q->mode = EQUIT; - } else { - Q->mode = OBLIQ; - Q->sinph0 = sin(P->phi0); - Q->cosph0 = cos(P->phi0); - } - - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} diff --git a/src/PJ_gnom.cpp b/src/PJ_gnom.cpp new file mode 100644 index 00000000..7313643f --- /dev/null +++ b/src/PJ_gnom.cpp @@ -0,0 +1,143 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(gnom, "Gnomonic") "\n\tAzi, Sph"; + +#define EPS10 1.e-10 + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double sinph0; + double cosph0; + enum Mode mode; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + + switch (Q->mode) { + case EQUIT: + xy.y = cosphi * coslam; + break; + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; + break; + case S_POLE: + xy.y = - sinphi; + break; + case N_POLE: + xy.y = sinphi; + break; + } + + if (xy.y <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + xy.x = (xy.y = 1. / xy.y) * cosphi * sin(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y *= sinphi; + break; + case OBLIQ: + xy.y *= Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; + break; + case N_POLE: + coslam = - coslam; + /*-fallthrough*/ + case S_POLE: + xy.y *= cosphi * coslam; + break; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rh, cosz, sinz; + + rh = hypot(xy.x, xy.y); + sinz = sin(lp.phi = atan(rh)); + cosz = sqrt(1. - sinz * sinz); + + if (fabs(rh) <= EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + } else { + switch (Q->mode) { + case OBLIQ: + lp.phi = cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh; + if (fabs(lp.phi) >= 1.) + lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; + else + lp.phi = asin(lp.phi); + xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; + xy.x *= sinz * Q->cosph0; + break; + case EQUIT: + lp.phi = xy.y * sinz / rh; + if (fabs(lp.phi) >= 1.) + lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; + else + lp.phi = asin(lp.phi); + xy.y = cosz * rh; + xy.x *= sinz; + break; + case S_POLE: + lp.phi -= M_HALFPI; + break; + case N_POLE: + lp.phi = M_HALFPI - lp.phi; + xy.y = -xy.y; + break; + } + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +PJ *PROJECTION(gnom) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) { + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + } else if (fabs(P->phi0) < EPS10) { + Q->mode = EQUIT; + } else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} diff --git a/src/PJ_goode.c b/src/PJ_goode.c deleted file mode 100644 index 8d8ac24d..00000000 --- a/src/PJ_goode.c +++ /dev/null @@ -1,82 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(goode, "Goode Homolosine") "\n\tPCyl, Sph"; - -#define Y_COR 0.05280 -#define PHI_LIM 0.71093078197902358062 - -C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *); - -struct pj_opaque { - PJ *sinu; - PJ *moll; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy; - struct pj_opaque *Q = P->opaque; - - if (fabs(lp.phi) <= PHI_LIM) - xy = Q->sinu->fwd(lp, Q->sinu); - else { - xy = Q->moll->fwd(lp, Q->moll); - xy.y -= lp.phi >= 0.0 ? Y_COR : -Y_COR; - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp; - struct pj_opaque *Q = P->opaque; - - if (fabs(xy.y) <= PHI_LIM) - lp = Q->sinu->inv(xy, Q->sinu); - else { - xy.y += xy.y >= 0.0 ? Y_COR : -Y_COR; - lp = Q->moll->inv(xy, Q->moll); - } - return lp; -} - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - if (0==P->opaque) - return pj_default_destructor (P, errlev); - pj_free (P->opaque->sinu); - pj_free (P->opaque->moll); - return pj_default_destructor (P, errlev); -} - - - -PJ *PROJECTION(goode) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - P->es = 0.; - if (!(Q->sinu = pj_sinu(0)) || !(Q->moll = pj_moll(0))) - return destructor (P, ENOMEM); - Q->sinu->es = 0.; - Q->sinu->ctx = P->ctx; - Q->moll->ctx = P->ctx; - if (!(Q->sinu = pj_sinu(Q->sinu)) || !(Q->moll = pj_moll(Q->moll))) - return destructor (P, ENOMEM); - - P->fwd = s_forward; - P->inv = s_inverse; - - return P; -} diff --git a/src/PJ_goode.cpp b/src/PJ_goode.cpp new file mode 100644 index 00000000..3f5a4f8c --- /dev/null +++ b/src/PJ_goode.cpp @@ -0,0 +1,82 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(goode, "Goode Homolosine") "\n\tPCyl, Sph"; + +#define Y_COR 0.05280 +#define PHI_LIM 0.71093078197902358062 + +C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *); + +struct pj_opaque { + PJ *sinu; + PJ *moll; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy; + struct pj_opaque *Q = static_cast(P->opaque); + + if (fabs(lp.phi) <= PHI_LIM) + xy = Q->sinu->fwd(lp, Q->sinu); + else { + xy = Q->moll->fwd(lp, Q->moll); + xy.y -= lp.phi >= 0.0 ? Y_COR : -Y_COR; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp; + struct pj_opaque *Q = static_cast(P->opaque); + + if (fabs(xy.y) <= PHI_LIM) + lp = Q->sinu->inv(xy, Q->sinu); + else { + xy.y += xy.y >= 0.0 ? Y_COR : -Y_COR; + lp = Q->moll->inv(xy, Q->moll); + } + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_default_destructor (P, errlev); + pj_free (static_cast(P->opaque)->sinu); + pj_free (static_cast(P->opaque)->moll); + return pj_default_destructor (P, errlev); +} + + + +PJ *PROJECTION(goode) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + P->es = 0.; + if (!(Q->sinu = pj_sinu(0)) || !(Q->moll = pj_moll(0))) + return destructor (P, ENOMEM); + Q->sinu->es = 0.; + Q->sinu->ctx = P->ctx; + Q->moll->ctx = P->ctx; + if (!(Q->sinu = pj_sinu(Q->sinu)) || !(Q->moll = pj_moll(Q->moll))) + return destructor (P, ENOMEM); + + P->fwd = s_forward; + P->inv = s_inverse; + + return P; +} diff --git a/src/PJ_gstmerc.c b/src/PJ_gstmerc.c deleted file mode 100644 index 5b5109b0..00000000 --- a/src/PJ_gstmerc.c +++ /dev/null @@ -1,72 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(gstmerc, "Gauss-Schreiber Transverse Mercator (aka Gauss-Laborde Reunion)") - "\n\tCyl, Sph&Ell\n\tlat_0= lon_0= k_0="; - -struct pj_opaque { - double lamc; - double phic; - double c; - double n1; - double n2; - double XS; - double YS; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double L, Ls, sinLs1, Ls1; - - L = Q->n1*lp.lam; - Ls = Q->c + Q->n1 * log(pj_tsfn(-1.0 * lp.phi, -1.0 * sin(lp.phi), P->e)); - sinLs1 = sin(L) / cosh(Ls); - Ls1 = log(pj_tsfn(-1.0 * asin(sinLs1), 0.0, 0.0)); - xy.x = (Q->XS + Q->n2*Ls1) * P->ra; - xy.y = (Q->YS + Q->n2*atan(sinh(Ls) / cos(L))) * P->ra; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double L, LC, sinC; - - L = atan(sinh((xy.x * P->a - Q->XS) / Q->n2) / cos((xy.y * P->a - Q->YS) / Q->n2)); - sinC = sin((xy.y * P->a - Q->YS) / Q->n2) / cosh((xy.x * P->a - Q->XS) / Q->n2); - LC = log(pj_tsfn(-1.0 * asin(sinC), 0.0, 0.0)); - lp.lam = L / Q->n1; - lp.phi = -1.0 * pj_phi2(P->ctx, exp((LC - Q->c) / Q->n1), P->e); - - return lp; -} - - -PJ *PROJECTION(gstmerc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->lamc = P->lam0; - Q->n1 = sqrt(1.0 + P->es * pow(cos(P->phi0), 4.0) / (1.0 - P->es)); - Q->phic = asin(sin(P->phi0) / Q->n1); - Q->c = log(pj_tsfn(-1.0 * Q->phic, 0.0, 0.0)) - - Q->n1 * log(pj_tsfn(-1.0 * P->phi0, -1.0 * sin(P->phi0), P->e)); - Q->n2 = P->k0 * P->a * sqrt(1.0 - P->es) / (1.0 - P->es * sin(P->phi0) * sin(P->phi0)); - Q->XS = 0; - Q->YS = -1.0 * Q->n2 * Q->phic; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_gstmerc.cpp b/src/PJ_gstmerc.cpp new file mode 100644 index 00000000..6475f972 --- /dev/null +++ b/src/PJ_gstmerc.cpp @@ -0,0 +1,72 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(gstmerc, "Gauss-Schreiber Transverse Mercator (aka Gauss-Laborde Reunion)") + "\n\tCyl, Sph&Ell\n\tlat_0= lon_0= k_0="; + +struct pj_opaque { + double lamc; + double phic; + double c; + double n1; + double n2; + double XS; + double YS; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double L, Ls, sinLs1, Ls1; + + L = Q->n1*lp.lam; + Ls = Q->c + Q->n1 * log(pj_tsfn(-1.0 * lp.phi, -1.0 * sin(lp.phi), P->e)); + sinLs1 = sin(L) / cosh(Ls); + Ls1 = log(pj_tsfn(-1.0 * asin(sinLs1), 0.0, 0.0)); + xy.x = (Q->XS + Q->n2*Ls1) * P->ra; + xy.y = (Q->YS + Q->n2*atan(sinh(Ls) / cos(L))) * P->ra; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double L, LC, sinC; + + L = atan(sinh((xy.x * P->a - Q->XS) / Q->n2) / cos((xy.y * P->a - Q->YS) / Q->n2)); + sinC = sin((xy.y * P->a - Q->YS) / Q->n2) / cosh((xy.x * P->a - Q->XS) / Q->n2); + LC = log(pj_tsfn(-1.0 * asin(sinC), 0.0, 0.0)); + lp.lam = L / Q->n1; + lp.phi = -1.0 * pj_phi2(P->ctx, exp((LC - Q->c) / Q->n1), P->e); + + return lp; +} + + +PJ *PROJECTION(gstmerc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->lamc = P->lam0; + Q->n1 = sqrt(1.0 + P->es * pow(cos(P->phi0), 4.0) / (1.0 - P->es)); + Q->phic = asin(sin(P->phi0) / Q->n1); + Q->c = log(pj_tsfn(-1.0 * Q->phic, 0.0, 0.0)) + - Q->n1 * log(pj_tsfn(-1.0 * P->phi0, -1.0 * sin(P->phi0), P->e)); + Q->n2 = P->k0 * P->a * sqrt(1.0 - P->es) / (1.0 - P->es * sin(P->phi0) * sin(P->phi0)); + Q->XS = 0; + Q->YS = -1.0 * Q->n2 * Q->phic; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_hammer.c b/src/PJ_hammer.c deleted file mode 100644 index f3e0d64e..00000000 --- a/src/PJ_hammer.c +++ /dev/null @@ -1,75 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(hammer, "Hammer & Eckert-Greifendorff") - "\n\tMisc Sph, \n\tW= M="; - -#define EPS 1.0e-10 - -struct pj_opaque { - double w; - double m, rm; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosphi, d; - - d = sqrt(2./(1. + (cosphi = cos(lp.phi)) * cos(lp.lam *= Q->w))); - xy.x = Q->m * d * cosphi * sin(lp.lam); - xy.y = Q->rm * d * sin(lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double z; - - z = sqrt(1. - 0.25*Q->w*Q->w*xy.x*xy.x - 0.25*xy.y*xy.y); - if (fabs(2.*z*z-1.) < EPS) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - proj_errno_set(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - } else { - lp.lam = aatan2(Q->w * xy.x * z,2. * z * z - 1)/Q->w; - lp.phi = aasin(P->ctx,z * xy.y); - } - return lp; -} - - -PJ *PROJECTION(hammer) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if (pj_param(P->ctx, P->params, "tW").i) { - if ((Q->w = fabs(pj_param(P->ctx, P->params, "dW").f)) <= 0.) - return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS); - } else - Q->w = .5; - if (pj_param(P->ctx, P->params, "tM").i) { - if ((Q->m = fabs(pj_param(P->ctx, P->params, "dM").f)) <= 0.) - return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS); - } else - Q->m = 1.; - - Q->rm = 1. / Q->m; - Q->m /= Q->w; - - P->es = 0.; - P->fwd = s_forward; - P->inv = s_inverse; - - return P; -} diff --git a/src/PJ_hammer.cpp b/src/PJ_hammer.cpp new file mode 100644 index 00000000..474d44ca --- /dev/null +++ b/src/PJ_hammer.cpp @@ -0,0 +1,75 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(hammer, "Hammer & Eckert-Greifendorff") + "\n\tMisc Sph, \n\tW= M="; + +#define EPS 1.0e-10 + +struct pj_opaque { + double w; + double m, rm; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosphi, d; + + d = sqrt(2./(1. + (cosphi = cos(lp.phi)) * cos(lp.lam *= Q->w))); + xy.x = Q->m * d * cosphi * sin(lp.lam); + xy.y = Q->rm * d * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double z; + + z = sqrt(1. - 0.25*Q->w*Q->w*xy.x*xy.x - 0.25*xy.y*xy.y); + if (fabs(2.*z*z-1.) < EPS) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + proj_errno_set(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + } else { + lp.lam = aatan2(Q->w * xy.x * z,2. * z * z - 1)/Q->w; + lp.phi = aasin(P->ctx,z * xy.y); + } + return lp; +} + + +PJ *PROJECTION(hammer) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if (pj_param(P->ctx, P->params, "tW").i) { + if ((Q->w = fabs(pj_param(P->ctx, P->params, "dW").f)) <= 0.) + return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS); + } else + Q->w = .5; + if (pj_param(P->ctx, P->params, "tM").i) { + if ((Q->m = fabs(pj_param(P->ctx, P->params, "dM").f)) <= 0.) + return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS); + } else + Q->m = 1.; + + Q->rm = 1. / Q->m; + Q->m /= Q->w; + + P->es = 0.; + P->fwd = s_forward; + P->inv = s_inverse; + + return P; +} diff --git a/src/PJ_hatano.c b/src/PJ_hatano.c deleted file mode 100644 index 019671cc..00000000 --- a/src/PJ_hatano.c +++ /dev/null @@ -1,83 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(hatano, "Hatano Asymmetrical Equal Area") "\n\tPCyl, Sph"; - -#define NITER 20 -#define EPS 1e-7 -#define ONETOL 1.000001 -#define CN 2.67595 -#define CS 2.43763 -#define RCN 0.37369906014686373063 -#define RCS 0.41023453108141924738 -#define FYCN 1.75859 -#define FYCS 1.93052 -#define RYCN 0.56863737426006061674 -#define RYCS 0.51799515156538134803 -#define FXC 0.85 -#define RXC 1.17647058823529411764 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double th1, c; - int i; - (void) P; - - c = sin(lp.phi) * (lp.phi < 0. ? CS : CN); - for (i = NITER; i; --i) { - lp.phi -= th1 = (lp.phi + sin(lp.phi) - c) / (1. + cos(lp.phi)); - if (fabs(th1) < EPS) break; - } - xy.x = FXC * lp.lam * cos(lp.phi *= .5); - xy.y = sin(lp.phi) * (lp.phi < 0. ? FYCS : FYCN); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double th; - - th = xy.y * ( xy.y < 0. ? RYCS : RYCN); - if (fabs(th) > 1.) { - if (fabs(th) > ONETOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - th = th > 0. ? M_HALFPI : - M_HALFPI; - } - } else { - th = asin(th); - } - - lp.lam = RXC * xy.x / cos(th); - th += th; - lp.phi = (th + sin(th)) * (xy.y < 0. ? RCS : RCN); - if (fabs(lp.phi) > 1.) { - if (fabs(lp.phi) > ONETOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; - } - } else { - lp.phi = asin(lp.phi); - } - - return (lp); -} - - -PJ *PROJECTION(hatano) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_hatano.cpp b/src/PJ_hatano.cpp new file mode 100644 index 00000000..019671cc --- /dev/null +++ b/src/PJ_hatano.cpp @@ -0,0 +1,83 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(hatano, "Hatano Asymmetrical Equal Area") "\n\tPCyl, Sph"; + +#define NITER 20 +#define EPS 1e-7 +#define ONETOL 1.000001 +#define CN 2.67595 +#define CS 2.43763 +#define RCN 0.37369906014686373063 +#define RCS 0.41023453108141924738 +#define FYCN 1.75859 +#define FYCS 1.93052 +#define RYCN 0.56863737426006061674 +#define RYCS 0.51799515156538134803 +#define FXC 0.85 +#define RXC 1.17647058823529411764 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double th1, c; + int i; + (void) P; + + c = sin(lp.phi) * (lp.phi < 0. ? CS : CN); + for (i = NITER; i; --i) { + lp.phi -= th1 = (lp.phi + sin(lp.phi) - c) / (1. + cos(lp.phi)); + if (fabs(th1) < EPS) break; + } + xy.x = FXC * lp.lam * cos(lp.phi *= .5); + xy.y = sin(lp.phi) * (lp.phi < 0. ? FYCS : FYCN); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double th; + + th = xy.y * ( xy.y < 0. ? RYCS : RYCN); + if (fabs(th) > 1.) { + if (fabs(th) > ONETOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + th = th > 0. ? M_HALFPI : - M_HALFPI; + } + } else { + th = asin(th); + } + + lp.lam = RXC * xy.x / cos(th); + th += th; + lp.phi = (th + sin(th)) * (xy.y < 0. ? RCS : RCN); + if (fabs(lp.phi) > 1.) { + if (fabs(lp.phi) > ONETOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI; + } + } else { + lp.phi = asin(lp.phi); + } + + return (lp); +} + + +PJ *PROJECTION(hatano) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_healpix.c b/src/PJ_healpix.c deleted file mode 100644 index 3a88695f..00000000 --- a/src/PJ_healpix.c +++ /dev/null @@ -1,672 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the HEALPix and rHEALPix projections. - * For background see . - * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) - * Michael Speth (spethm@landcareresearch.co.nz) - * Notes: Raichev implemented these projections in Python and - * Speth translated them into C here. - ****************************************************************************** - * Copyright (c) 2001, Thomas Flemming, tf@ttqv.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 substcounteral 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. - *****************************************************************************/ -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(healpix, "HEALPix") "\n\tSph&Ell"; -PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph&Ell\n\tnorth_square= south_square="; - -/* Matrix for counterclockwise rotation by pi/2: */ -# define R1 {{ 0,-1},{ 1, 0}} -/* Matrix for counterclockwise rotation by pi: */ -# define R2 {{-1, 0},{ 0,-1}} -/* Matrix for counterclockwise rotation by 3*pi/2: */ -# define R3 {{ 0, 1},{-1, 0}} -/* Identity matrix */ -# define IDENT {{1, 0},{0, 1}} -/* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/ -# define ROT {IDENT, R1, R2, R3, R3, R2, R1} -/* Fuzz to handle rounding errors: */ -# define EPS 1e-15 - -struct pj_opaque { - int north_square; - int south_square; - double qp; - double *apa; -}; - -typedef struct { - int cn; /* An integer 0--3 indicating the position of the polar cap. */ - double x, y; /* Coordinates of the pole point (point of most extreme latitude on the polar caps). */ - enum Region {north, south, equatorial} region; -} CapMap; - -static const double rot[7][2][2] = ROT; - -/** - * Returns the sign of the double. - * @param v the parameter whose sign is returned. - * @return 1 for positive number, -1 for negative, and 0 for zero. - **/ -static double sign (double v) { - return v > 0 ? 1 : (v < 0 ? -1 : 0); -} - - -/** - * Return the index of the matrix in ROT. - * @param index ranges from -3 to 3. - */ -static int get_rotate_index(int index) { - switch(index) { - case 0: - return 0; - case 1: - return 1; - case 2: - return 2; - case 3: - return 3; - case -1: - return 4; - case -2: - return 5; - case -3: - return 6; - } - return 0; -} - - -/** - * Return 1 if point (testx, testy) lies in the interior of the polygon - * determined by the vertices in vert, and return 0 otherwise. - * See http://paulbourke.net/geometry/polygonmesh/ for more details. - * @param nvert the number of vertices in the polygon. - * @param vert the (x, y)-coordinates of the polygon's vertices - **/ -static int pnpoly(int nvert, double vert[][2], double testx, double testy) { - int i; - int counter = 0; - double xinters; - XY p1, p2; - - /* Check for boundrary cases */ - for (i = 0; i < nvert; i++) { - if (testx == vert[i][0] && testy == vert[i][1]) { - return 1; - } - } - - p1.x = vert[0][0]; - p1.y = vert[0][1]; - - for (i = 1; i < nvert; i++) { - p2.x = vert[i % nvert][0]; - p2.y = vert[i % nvert][1]; - if (testy > MIN(p1.y, p2.y) && - testy <= MAX(p1.y, p2.y) && - testx <= MAX(p1.x, p2.x) && - p1.y != p2.y) - { - xinters = (testy-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; - if (p1.x == p2.x || testx <= xinters) - counter++; - } - p1 = p2; - } - - if (counter % 2 == 0) { - return 0; - } else { - return 1; - } -} - - -/** - * Return 1 if (x, y) lies in (the interior or boundary of) the image of the - * HEALPix projection (in case proj=0) or in the image the rHEALPix projection - * (in case proj=1), and return 0 otherwise. - * @param north_square the position of the north polar square (rHEALPix only) - * @param south_square the position of the south polar square (rHEALPix only) - **/ -static int in_image(double x, double y, int proj, int north_square, - int south_square) { - if (proj == 0) { - double healpixVertsJit[][2] = { - {-M_PI - EPS, M_FORTPI}, - {-3*M_FORTPI, M_HALFPI + EPS}, - {-M_HALFPI, M_FORTPI + EPS}, - {-M_FORTPI, M_HALFPI + EPS}, - {0.0, M_FORTPI + EPS}, - {M_FORTPI, M_HALFPI + EPS}, - {M_HALFPI, M_FORTPI + EPS}, - {3*M_FORTPI, M_HALFPI + EPS}, - {M_PI + EPS, M_FORTPI}, - {M_PI + EPS, -M_FORTPI}, - {3*M_FORTPI, -M_HALFPI - EPS}, - {M_HALFPI, -M_FORTPI - EPS}, - {M_FORTPI, -M_HALFPI - EPS}, - {0.0, -M_FORTPI - EPS}, - {-M_FORTPI, -M_HALFPI - EPS}, - {-M_HALFPI, -M_FORTPI - EPS}, - {-3*M_FORTPI, -M_HALFPI - EPS}, - {-M_PI - EPS, -M_FORTPI} - }; - return pnpoly((int)sizeof(healpixVertsJit)/ - sizeof(healpixVertsJit[0]), healpixVertsJit, x, y); - } else { - /** - * Assigning each element by index to avoid warnings such as - * 'initializer element is not computable at load time'. - * Before C99 this was not allowed and to keep as portable as - * possible we do it the C89 way here. - * We need to assign the array this way because the input is - * dynamic (north_square and south_square vars are unknown at - * compile time). - **/ - double rhealpixVertsJit[12][2]; - rhealpixVertsJit[0][0] = -M_PI - EPS; - rhealpixVertsJit[0][1] = M_FORTPI + EPS; - rhealpixVertsJit[1][0] = -M_PI + north_square*M_HALFPI- EPS; - rhealpixVertsJit[1][1] = M_FORTPI + EPS; - rhealpixVertsJit[2][0] = -M_PI + north_square*M_HALFPI- EPS; - rhealpixVertsJit[2][1] = 3*M_FORTPI + EPS; - rhealpixVertsJit[3][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS; - rhealpixVertsJit[3][1] = 3*M_FORTPI + EPS; - rhealpixVertsJit[4][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS; - rhealpixVertsJit[4][1] = M_FORTPI + EPS; - rhealpixVertsJit[5][0] = M_PI + EPS; - rhealpixVertsJit[5][1] = M_FORTPI + EPS; - rhealpixVertsJit[6][0] = M_PI + EPS; - rhealpixVertsJit[6][1] = -M_FORTPI - EPS; - rhealpixVertsJit[7][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS; - rhealpixVertsJit[7][1] = -M_FORTPI - EPS; - rhealpixVertsJit[8][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS; - rhealpixVertsJit[8][1] = -3*M_FORTPI - EPS; - rhealpixVertsJit[9][0] = -M_PI + south_square*M_HALFPI - EPS; - rhealpixVertsJit[9][1] = -3*M_FORTPI - EPS; - rhealpixVertsJit[10][0] = -M_PI + south_square*M_HALFPI - EPS; - rhealpixVertsJit[10][1] = -M_FORTPI - EPS; - rhealpixVertsJit[11][0] = -M_PI - EPS; - rhealpixVertsJit[11][1] = -M_FORTPI - EPS; - - return pnpoly((int)sizeof(rhealpixVertsJit)/ - sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y); - } -} - - -/** - * Return the authalic latitude of latitude alpha (if inverse=0) or - * return the approximate latitude of authalic latitude alpha (if inverse=1). - * P contains the relevant ellipsoid parameters. - **/ -static double auth_lat(PJ *P, double alpha, int inverse) { - struct pj_opaque *Q = P->opaque; - if (inverse == 0) { - /* Authalic latitude. */ - double q = pj_qsfn(sin(alpha), P->e, 1.0 - P->es); - double qp = Q->qp; - double ratio = q/qp; - - if (fabs(ratio) > 1) { - /* Rounding error. */ - ratio = sign(ratio); - } - return asin(ratio); - } else { - /* Approximation to inverse authalic latitude. */ - return pj_authlat(alpha, Q->apa); - } -} - - -/** - * Return the HEALPix projection of the longitude-latitude point lp on - * the unit sphere. -**/ -static XY healpix_sphere(LP lp) { - double lam = lp.lam; - double phi = lp.phi; - double phi0 = asin(2.0/3.0); - XY xy; - - /* equatorial region */ - if ( fabs(phi) <= phi0) { - xy.x = lam; - xy.y = 3*M_PI/8*sin(phi); - } else { - double lamc; - double sigma = sqrt(3*(1 - fabs(sin(phi)))); - double cn = floor(2*lam / M_PI + 2); - if (cn >= 4) { - cn = 3; - } - lamc = -3*M_FORTPI + M_HALFPI*cn; - xy.x = lamc + (lam - lamc)*sigma; - xy.y = sign(phi)*M_FORTPI*(2 - sigma); - } - return xy; -} - - -/** - * Return the inverse of healpix_sphere(). -**/ -static LP healpix_sphere_inverse(XY xy) { - LP lp; - double x = xy.x; - double y = xy.y; - double y0 = M_FORTPI; - - /* Equatorial region. */ - if (fabs(y) <= y0) { - lp.lam = x; - lp.phi = asin(8*y/(3*M_PI)); - } else if (fabs(y) < M_HALFPI) { - double cn = floor(2*x/M_PI + 2); - double xc, tau; - if (cn >= 4) { - cn = 3; - } - xc = -3*M_FORTPI + M_HALFPI*cn; - tau = 2.0 - 4*fabs(y)/M_PI; - lp.lam = xc + (x - xc)/tau; - lp.phi = sign(y)*asin(1.0 - pow(tau, 2)/3.0); - } else { - lp.lam = -M_PI; - lp.phi = sign(y)*M_HALFPI; - } - return (lp); -} - - -/** - * Return the vector sum a + b, where a and b are 2-dimensional vectors. - * @param ret holds a + b. - **/ -static void vector_add(const double a[2], const double b[2], double *ret) { - int i; - for(i = 0; i < 2; i++) { - ret[i] = a[i] + b[i]; - } -} - - -/** - * Return the vector difference a - b, where a and b are 2-dimensional vectors. - * @param ret holds a - b. - **/ -static void vector_sub(const double a[2], const double b[2], double*ret) { - int i; - for(i = 0; i < 2; i++) { - ret[i] = a[i] - b[i]; - } -} - - -/** - * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and - * b is a 2 x 1 matrix. - * @param ret holds a*b. - **/ -static void dot_product(const double a[2][2], const double b[2], double *ret) { - int i, j; - int length = 2; - for(i = 0; i < length; i++) { - ret[i] = 0; - for(j = 0; j < length; j++) { - ret[i] += a[i][j]*b[j]; - } - } -} - - -/** - * Return the number of the polar cap, the pole point coordinates, and - * the region that (x, y) lies in. - * If inverse=0, then assume (x,y) lies in the image of the HEALPix - * projection of the unit sphere. - * If inverse=1, then assume (x,y) lies in the image of the - * (north_square, south_square)-rHEALPix projection of the unit sphere. - **/ -static CapMap get_cap(double x, double y, int north_square, int south_square, - int inverse) { - CapMap capmap; - double c; - - capmap.x = x; - capmap.y = y; - if (inverse == 0) { - if (y > M_FORTPI) { - capmap.region = north; - c = M_HALFPI; - } else if (y < -M_FORTPI) { - capmap.region = south; - c = -M_HALFPI; - } else { - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - /* polar region */ - if (x < -M_HALFPI) { - capmap.cn = 0; - capmap.x = (-3*M_FORTPI); - capmap.y = c; - } else if (x >= -M_HALFPI && x < 0) { - capmap.cn = 1; - capmap.x = -M_FORTPI; - capmap.y = c; - } else if (x >= 0 && x < M_HALFPI) { - capmap.cn = 2; - capmap.x = M_FORTPI; - capmap.y = c; - } else { - capmap.cn = 3; - capmap.x = 3*M_FORTPI; - capmap.y = c; - } - } else { - if (y > M_FORTPI) { - capmap.region = north; - capmap.x = -3*M_FORTPI + north_square*M_HALFPI; - capmap.y = M_HALFPI; - x = x - north_square*M_HALFPI; - } else if (y < -M_FORTPI) { - capmap.region = south; - capmap.x = -3*M_FORTPI + south_square*M_HALFPI; - capmap.y = -M_HALFPI; - x = x - south_square*M_HALFPI; - } else { - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - /* Polar Region, find the HEALPix polar cap number that - x, y moves to when rHEALPix polar square is disassembled. */ - if (capmap.region == north) { - if (y >= -x - M_FORTPI - EPS && y < x + 5*M_FORTPI - EPS) { - capmap.cn = (north_square + 1) % 4; - } else if (y > -x -M_FORTPI + EPS && y >= x + 5*M_FORTPI - EPS) { - capmap.cn = (north_square + 2) % 4; - } else if (y <= -x -M_FORTPI + EPS && y > x + 5*M_FORTPI + EPS) { - capmap.cn = (north_square + 3) % 4; - } else { - capmap.cn = north_square; - } - } else if (capmap.region == south) { - if (y <= x + M_FORTPI + EPS && y > -x - 5*M_FORTPI + EPS) { - capmap.cn = (south_square + 1) % 4; - } else if (y < x + M_FORTPI - EPS && y <= -x - 5*M_FORTPI + EPS) { - capmap.cn = (south_square + 2) % 4; - } else if (y >= x + M_FORTPI - EPS && y < -x - 5*M_FORTPI - EPS) { - capmap.cn = (south_square + 3) % 4; - } else { - capmap.cn = south_square; - } - } - } - return capmap; -} - - -/** - * Rearrange point (x, y) in the HEALPix projection by - * combining the polar caps into two polar squares. - * Put the north polar square in position north_square and - * the south polar square in position south_square. - * If inverse=1, then uncombine the polar caps. - * @param north_square integer between 0 and 3. - * @param south_square integer between 0 and 3. - **/ -static XY combine_caps(double x, double y, int north_square, int south_square, - int inverse) { - XY xy; - double v[2]; - double c[2]; - double vector[2]; - double v_min_c[2]; - double ret_dot[2]; - const double (*tmpRot)[2]; - int pole = 0; - - CapMap capmap = get_cap(x, y, north_square, south_square, inverse); - if (capmap.region == equatorial) { - xy.x = capmap.x; - xy.y = capmap.y; - return xy; - } - - v[0] = x; v[1] = y; - c[0] = capmap.x; c[1] = capmap.y; - - if (inverse == 0) { - /* Rotate (x, y) about its polar cap tip and then translate it to - north_square or south_square. */ - - if (capmap.region == north) { - pole = north_square; - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - } else { - pole = south_square; - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - } - } else { - /* Inverse function. - Unrotate (x, y) and then translate it back. */ - - /* disassemble */ - if (capmap.region == north) { - pole = north_square; - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - } else { - pole = south_square; - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - } - } - - vector_sub(v, c, v_min_c); - dot_product(tmpRot, v_min_c, ret_dot); - { - double a[2]; - /* Workaround cppcheck git issue */ - double* pa = a; - pa[0] = -3*M_FORTPI + ((inverse == 0) ? pole : capmap.cn) *M_HALFPI; - pa[1] = ((capmap.region == north) ? 1 : -1) *M_HALFPI; - vector_add(ret_dot, a, vector); - } - - xy.x = vector[0]; - xy.y = vector[1]; - return xy; -} - - -static XY s_healpix_forward(LP lp, PJ *P) { /* sphere */ - (void) P; - return healpix_sphere(lp); -} - - -static XY e_healpix_forward(LP lp, PJ *P) { /* ellipsoid */ - lp.phi = auth_lat(P, lp.phi, 0); - return healpix_sphere(lp); -} - - -static LP s_healpix_inverse(XY xy, PJ *P) { /* sphere */ - /* Check whether (x, y) lies in the HEALPix image */ - if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { - LP lp; - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); - return lp; - } - return healpix_sphere_inverse(xy); -} - - -static LP e_healpix_inverse(XY xy, PJ *P) { /* ellipsoid */ - LP lp = {0.0,0.0}; - - /* Check whether (x, y) lies in the HEALPix image. */ - if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); - return lp; - } - lp = healpix_sphere_inverse(xy); - lp.phi = auth_lat(P, lp.phi, 1); - return lp; -} - - -static XY s_rhealpix_forward(LP lp, PJ *P) { /* sphere */ - struct pj_opaque *Q = P->opaque; - - XY xy = healpix_sphere(lp); - return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); -} - - -static XY e_rhealpix_forward(LP lp, PJ *P) { /* ellipsoid */ - struct pj_opaque *Q = P->opaque; - XY xy; - lp.phi = auth_lat(P, lp.phi, 0); - xy = healpix_sphere(lp); - return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); -} - - -static LP s_rhealpix_inverse(XY xy, PJ *P) { /* sphere */ - struct pj_opaque *Q = P->opaque; - - /* Check whether (x, y) lies in the rHEALPix image. */ - if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { - LP lp; - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); - return lp; - } - xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); - return healpix_sphere_inverse(xy); -} - - -static LP e_rhealpix_inverse(XY xy, PJ *P) { /* ellipsoid */ - struct pj_opaque *Q = P->opaque; - LP lp = {0.0,0.0}; - - /* Check whether (x, y) lies in the rHEALPix image. */ - if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); - return lp; - } - xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); - lp = healpix_sphere_inverse(xy); - lp.phi = auth_lat(P, lp.phi, 1); - return lp; -} - - -static void *destructor (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->apa); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(healpix) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - if (P->es != 0.0) { - Q->apa = pj_authset(P->es); /* For auth_lat(). */ - if (0==Q->apa) - return destructor(P, ENOMEM); - Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ - P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ - pj_calc_ellipsoid_params (P, P->a, P->es); /* Ensure we have a consistent parameter set */ - P->fwd = e_healpix_forward; - P->inv = e_healpix_inverse; - } else { - P->fwd = s_healpix_forward; - P->inv = s_healpix_inverse; - } - - return P; -} - - -PJ *PROJECTION(rhealpix) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - Q->north_square = pj_param(P->ctx, P->params,"inorth_square").i; - Q->south_square = pj_param(P->ctx, P->params,"isouth_square").i; - - /* Check for valid north_square and south_square inputs. */ - if (Q->north_square < 0 || Q->north_square > 3) - return destructor (P, PJD_ERR_AXIS); - if (Q->south_square < 0 || Q->south_square > 3) - return destructor (P, PJD_ERR_AXIS); - if (P->es != 0.0) { - Q->apa = pj_authset(P->es); /* For auth_lat(). */ - if (0==Q->apa) - return destructor(P, ENOMEM); - Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ - P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ - P->ra = 1.0/P->a; - P->fwd = e_rhealpix_forward; - P->inv = e_rhealpix_inverse; - } else { - P->fwd = s_rhealpix_forward; - P->inv = s_rhealpix_inverse; - } - - return P; -} diff --git a/src/PJ_healpix.cpp b/src/PJ_healpix.cpp new file mode 100644 index 00000000..a7d42398 --- /dev/null +++ b/src/PJ_healpix.cpp @@ -0,0 +1,672 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the HEALPix and rHEALPix projections. + * For background see . + * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) + * Michael Speth (spethm@landcareresearch.co.nz) + * Notes: Raichev implemented these projections in Python and + * Speth translated them into C here. + ****************************************************************************** + * Copyright (c) 2001, Thomas Flemming, tf@ttqv.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 substcounteral 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. + *****************************************************************************/ +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(healpix, "HEALPix") "\n\tSph&Ell"; +PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph&Ell\n\tnorth_square= south_square="; + +/* Matrix for counterclockwise rotation by pi/2: */ +# define R1 {{ 0,-1},{ 1, 0}} +/* Matrix for counterclockwise rotation by pi: */ +# define R2 {{-1, 0},{ 0,-1}} +/* Matrix for counterclockwise rotation by 3*pi/2: */ +# define R3 {{ 0, 1},{-1, 0}} +/* Identity matrix */ +# define IDENT {{1, 0},{0, 1}} +/* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/ +# define ROT {IDENT, R1, R2, R3, R3, R2, R1} +/* Fuzz to handle rounding errors: */ +# define EPS 1e-15 + +struct pj_opaque { + int north_square; + int south_square; + double qp; + double *apa; +}; + +typedef struct { + int cn; /* An integer 0--3 indicating the position of the polar cap. */ + double x, y; /* Coordinates of the pole point (point of most extreme latitude on the polar caps). */ + enum Region {north, south, equatorial} region; +} CapMap; + +static const double rot[7][2][2] = ROT; + +/** + * Returns the sign of the double. + * @param v the parameter whose sign is returned. + * @return 1 for positive number, -1 for negative, and 0 for zero. + **/ +static double sign (double v) { + return v > 0 ? 1 : (v < 0 ? -1 : 0); +} + + +/** + * Return the index of the matrix in ROT. + * @param index ranges from -3 to 3. + */ +static int get_rotate_index(int index) { + switch(index) { + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + case 3: + return 3; + case -1: + return 4; + case -2: + return 5; + case -3: + return 6; + } + return 0; +} + + +/** + * Return 1 if point (testx, testy) lies in the interior of the polygon + * determined by the vertices in vert, and return 0 otherwise. + * See http://paulbourke.net/geometry/polygonmesh/ for more details. + * @param nvert the number of vertices in the polygon. + * @param vert the (x, y)-coordinates of the polygon's vertices + **/ +static int pnpoly(int nvert, double vert[][2], double testx, double testy) { + int i; + int counter = 0; + double xinters; + XY p1, p2; + + /* Check for boundrary cases */ + for (i = 0; i < nvert; i++) { + if (testx == vert[i][0] && testy == vert[i][1]) { + return 1; + } + } + + p1.x = vert[0][0]; + p1.y = vert[0][1]; + + for (i = 1; i < nvert; i++) { + p2.x = vert[i % nvert][0]; + p2.y = vert[i % nvert][1]; + if (testy > MIN(p1.y, p2.y) && + testy <= MAX(p1.y, p2.y) && + testx <= MAX(p1.x, p2.x) && + p1.y != p2.y) + { + xinters = (testy-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; + if (p1.x == p2.x || testx <= xinters) + counter++; + } + p1 = p2; + } + + if (counter % 2 == 0) { + return 0; + } else { + return 1; + } +} + + +/** + * Return 1 if (x, y) lies in (the interior or boundary of) the image of the + * HEALPix projection (in case proj=0) or in the image the rHEALPix projection + * (in case proj=1), and return 0 otherwise. + * @param north_square the position of the north polar square (rHEALPix only) + * @param south_square the position of the south polar square (rHEALPix only) + **/ +static int in_image(double x, double y, int proj, int north_square, + int south_square) { + if (proj == 0) { + double healpixVertsJit[][2] = { + {-M_PI - EPS, M_FORTPI}, + {-3*M_FORTPI, M_HALFPI + EPS}, + {-M_HALFPI, M_FORTPI + EPS}, + {-M_FORTPI, M_HALFPI + EPS}, + {0.0, M_FORTPI + EPS}, + {M_FORTPI, M_HALFPI + EPS}, + {M_HALFPI, M_FORTPI + EPS}, + {3*M_FORTPI, M_HALFPI + EPS}, + {M_PI + EPS, M_FORTPI}, + {M_PI + EPS, -M_FORTPI}, + {3*M_FORTPI, -M_HALFPI - EPS}, + {M_HALFPI, -M_FORTPI - EPS}, + {M_FORTPI, -M_HALFPI - EPS}, + {0.0, -M_FORTPI - EPS}, + {-M_FORTPI, -M_HALFPI - EPS}, + {-M_HALFPI, -M_FORTPI - EPS}, + {-3*M_FORTPI, -M_HALFPI - EPS}, + {-M_PI - EPS, -M_FORTPI} + }; + return pnpoly((int)sizeof(healpixVertsJit)/ + sizeof(healpixVertsJit[0]), healpixVertsJit, x, y); + } else { + /** + * Assigning each element by index to avoid warnings such as + * 'initializer element is not computable at load time'. + * Before C99 this was not allowed and to keep as portable as + * possible we do it the C89 way here. + * We need to assign the array this way because the input is + * dynamic (north_square and south_square vars are unknown at + * compile time). + **/ + double rhealpixVertsJit[12][2]; + rhealpixVertsJit[0][0] = -M_PI - EPS; + rhealpixVertsJit[0][1] = M_FORTPI + EPS; + rhealpixVertsJit[1][0] = -M_PI + north_square*M_HALFPI- EPS; + rhealpixVertsJit[1][1] = M_FORTPI + EPS; + rhealpixVertsJit[2][0] = -M_PI + north_square*M_HALFPI- EPS; + rhealpixVertsJit[2][1] = 3*M_FORTPI + EPS; + rhealpixVertsJit[3][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS; + rhealpixVertsJit[3][1] = 3*M_FORTPI + EPS; + rhealpixVertsJit[4][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS; + rhealpixVertsJit[4][1] = M_FORTPI + EPS; + rhealpixVertsJit[5][0] = M_PI + EPS; + rhealpixVertsJit[5][1] = M_FORTPI + EPS; + rhealpixVertsJit[6][0] = M_PI + EPS; + rhealpixVertsJit[6][1] = -M_FORTPI - EPS; + rhealpixVertsJit[7][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS; + rhealpixVertsJit[7][1] = -M_FORTPI - EPS; + rhealpixVertsJit[8][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS; + rhealpixVertsJit[8][1] = -3*M_FORTPI - EPS; + rhealpixVertsJit[9][0] = -M_PI + south_square*M_HALFPI - EPS; + rhealpixVertsJit[9][1] = -3*M_FORTPI - EPS; + rhealpixVertsJit[10][0] = -M_PI + south_square*M_HALFPI - EPS; + rhealpixVertsJit[10][1] = -M_FORTPI - EPS; + rhealpixVertsJit[11][0] = -M_PI - EPS; + rhealpixVertsJit[11][1] = -M_FORTPI - EPS; + + return pnpoly((int)sizeof(rhealpixVertsJit)/ + sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y); + } +} + + +/** + * Return the authalic latitude of latitude alpha (if inverse=0) or + * return the approximate latitude of authalic latitude alpha (if inverse=1). + * P contains the relevant ellipsoid parameters. + **/ +static double auth_lat(PJ *P, double alpha, int inverse) { + struct pj_opaque *Q = static_cast(P->opaque); + if (inverse == 0) { + /* Authalic latitude. */ + double q = pj_qsfn(sin(alpha), P->e, 1.0 - P->es); + double qp = Q->qp; + double ratio = q/qp; + + if (fabs(ratio) > 1) { + /* Rounding error. */ + ratio = sign(ratio); + } + return asin(ratio); + } else { + /* Approximation to inverse authalic latitude. */ + return pj_authlat(alpha, Q->apa); + } +} + + +/** + * Return the HEALPix projection of the longitude-latitude point lp on + * the unit sphere. +**/ +static XY healpix_sphere(LP lp) { + double lam = lp.lam; + double phi = lp.phi; + double phi0 = asin(2.0/3.0); + XY xy; + + /* equatorial region */ + if ( fabs(phi) <= phi0) { + xy.x = lam; + xy.y = 3*M_PI/8*sin(phi); + } else { + double lamc; + double sigma = sqrt(3*(1 - fabs(sin(phi)))); + double cn = floor(2*lam / M_PI + 2); + if (cn >= 4) { + cn = 3; + } + lamc = -3*M_FORTPI + M_HALFPI*cn; + xy.x = lamc + (lam - lamc)*sigma; + xy.y = sign(phi)*M_FORTPI*(2 - sigma); + } + return xy; +} + + +/** + * Return the inverse of healpix_sphere(). +**/ +static LP healpix_sphere_inverse(XY xy) { + LP lp; + double x = xy.x; + double y = xy.y; + double y0 = M_FORTPI; + + /* Equatorial region. */ + if (fabs(y) <= y0) { + lp.lam = x; + lp.phi = asin(8*y/(3*M_PI)); + } else if (fabs(y) < M_HALFPI) { + double cn = floor(2*x/M_PI + 2); + double xc, tau; + if (cn >= 4) { + cn = 3; + } + xc = -3*M_FORTPI + M_HALFPI*cn; + tau = 2.0 - 4*fabs(y)/M_PI; + lp.lam = xc + (x - xc)/tau; + lp.phi = sign(y)*asin(1.0 - pow(tau, 2)/3.0); + } else { + lp.lam = -M_PI; + lp.phi = sign(y)*M_HALFPI; + } + return (lp); +} + + +/** + * Return the vector sum a + b, where a and b are 2-dimensional vectors. + * @param ret holds a + b. + **/ +static void vector_add(const double a[2], const double b[2], double *ret) { + int i; + for(i = 0; i < 2; i++) { + ret[i] = a[i] + b[i]; + } +} + + +/** + * Return the vector difference a - b, where a and b are 2-dimensional vectors. + * @param ret holds a - b. + **/ +static void vector_sub(const double a[2], const double b[2], double*ret) { + int i; + for(i = 0; i < 2; i++) { + ret[i] = a[i] - b[i]; + } +} + + +/** + * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and + * b is a 2 x 1 matrix. + * @param ret holds a*b. + **/ +static void dot_product(const double a[2][2], const double b[2], double *ret) { + int i, j; + int length = 2; + for(i = 0; i < length; i++) { + ret[i] = 0; + for(j = 0; j < length; j++) { + ret[i] += a[i][j]*b[j]; + } + } +} + + +/** + * Return the number of the polar cap, the pole point coordinates, and + * the region that (x, y) lies in. + * If inverse=0, then assume (x,y) lies in the image of the HEALPix + * projection of the unit sphere. + * If inverse=1, then assume (x,y) lies in the image of the + * (north_square, south_square)-rHEALPix projection of the unit sphere. + **/ +static CapMap get_cap(double x, double y, int north_square, int south_square, + int inverse) { + CapMap capmap; + double c; + + capmap.x = x; + capmap.y = y; + if (inverse == 0) { + if (y > M_FORTPI) { + capmap.region = CapMap::north; + c = M_HALFPI; + } else if (y < -M_FORTPI) { + capmap.region = CapMap::south; + c = -M_HALFPI; + } else { + capmap.region = CapMap::equatorial; + capmap.cn = 0; + return capmap; + } + /* polar region */ + if (x < -M_HALFPI) { + capmap.cn = 0; + capmap.x = (-3*M_FORTPI); + capmap.y = c; + } else if (x >= -M_HALFPI && x < 0) { + capmap.cn = 1; + capmap.x = -M_FORTPI; + capmap.y = c; + } else if (x >= 0 && x < M_HALFPI) { + capmap.cn = 2; + capmap.x = M_FORTPI; + capmap.y = c; + } else { + capmap.cn = 3; + capmap.x = 3*M_FORTPI; + capmap.y = c; + } + } else { + if (y > M_FORTPI) { + capmap.region = CapMap::north; + capmap.x = -3*M_FORTPI + north_square*M_HALFPI; + capmap.y = M_HALFPI; + x = x - north_square*M_HALFPI; + } else if (y < -M_FORTPI) { + capmap.region = CapMap::south; + capmap.x = -3*M_FORTPI + south_square*M_HALFPI; + capmap.y = -M_HALFPI; + x = x - south_square*M_HALFPI; + } else { + capmap.region = CapMap::equatorial; + capmap.cn = 0; + return capmap; + } + /* Polar Region, find the HEALPix polar cap number that + x, y moves to when rHEALPix polar square is disassembled. */ + if (capmap.region == CapMap::north) { + if (y >= -x - M_FORTPI - EPS && y < x + 5*M_FORTPI - EPS) { + capmap.cn = (north_square + 1) % 4; + } else if (y > -x -M_FORTPI + EPS && y >= x + 5*M_FORTPI - EPS) { + capmap.cn = (north_square + 2) % 4; + } else if (y <= -x -M_FORTPI + EPS && y > x + 5*M_FORTPI + EPS) { + capmap.cn = (north_square + 3) % 4; + } else { + capmap.cn = north_square; + } + } else if (capmap.region == CapMap::south) { + if (y <= x + M_FORTPI + EPS && y > -x - 5*M_FORTPI + EPS) { + capmap.cn = (south_square + 1) % 4; + } else if (y < x + M_FORTPI - EPS && y <= -x - 5*M_FORTPI + EPS) { + capmap.cn = (south_square + 2) % 4; + } else if (y >= x + M_FORTPI - EPS && y < -x - 5*M_FORTPI - EPS) { + capmap.cn = (south_square + 3) % 4; + } else { + capmap.cn = south_square; + } + } + } + return capmap; +} + + +/** + * Rearrange point (x, y) in the HEALPix projection by + * combining the polar caps into two polar squares. + * Put the north polar square in position north_square and + * the south polar square in position south_square. + * If inverse=1, then uncombine the polar caps. + * @param north_square integer between 0 and 3. + * @param south_square integer between 0 and 3. + **/ +static XY combine_caps(double x, double y, int north_square, int south_square, + int inverse) { + XY xy; + double v[2]; + double c[2]; + double vector[2]; + double v_min_c[2]; + double ret_dot[2]; + const double (*tmpRot)[2]; + int pole = 0; + + CapMap capmap = get_cap(x, y, north_square, south_square, inverse); + if (capmap.region == CapMap::equatorial) { + xy.x = capmap.x; + xy.y = capmap.y; + return xy; + } + + v[0] = x; v[1] = y; + c[0] = capmap.x; c[1] = capmap.y; + + if (inverse == 0) { + /* Rotate (x, y) about its polar cap tip and then translate it to + north_square or south_square. */ + + if (capmap.region == CapMap::north) { + pole = north_square; + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + } else { + pole = south_square; + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + } + } else { + /* Inverse function. + Unrotate (x, y) and then translate it back. */ + + /* disassemble */ + if (capmap.region == CapMap::north) { + pole = north_square; + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + } else { + pole = south_square; + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + } + } + + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + { + double a[2]; + /* Workaround cppcheck git issue */ + double* pa = a; + pa[0] = -3*M_FORTPI + ((inverse == 0) ? pole : capmap.cn) *M_HALFPI; + pa[1] = ((capmap.region == CapMap::north) ? 1 : -1) *M_HALFPI; + vector_add(ret_dot, a, vector); + } + + xy.x = vector[0]; + xy.y = vector[1]; + return xy; +} + + +static XY s_healpix_forward(LP lp, PJ *P) { /* sphere */ + (void) P; + return healpix_sphere(lp); +} + + +static XY e_healpix_forward(LP lp, PJ *P) { /* ellipsoid */ + lp.phi = auth_lat(P, lp.phi, 0); + return healpix_sphere(lp); +} + + +static LP s_healpix_inverse(XY xy, PJ *P) { /* sphere */ + /* Check whether (x, y) lies in the HEALPix image */ + if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { + LP lp; + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); + return lp; + } + return healpix_sphere_inverse(xy); +} + + +static LP e_healpix_inverse(XY xy, PJ *P) { /* ellipsoid */ + LP lp = {0.0,0.0}; + + /* Check whether (x, y) lies in the HEALPix image. */ + if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); + return lp; + } + lp = healpix_sphere_inverse(xy); + lp.phi = auth_lat(P, lp.phi, 1); + return lp; +} + + +static XY s_rhealpix_forward(LP lp, PJ *P) { /* sphere */ + struct pj_opaque *Q = static_cast(P->opaque); + + XY xy = healpix_sphere(lp); + return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); +} + + +static XY e_rhealpix_forward(LP lp, PJ *P) { /* ellipsoid */ + struct pj_opaque *Q = static_cast(P->opaque); + XY xy; + lp.phi = auth_lat(P, lp.phi, 0); + xy = healpix_sphere(lp); + return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); +} + + +static LP s_rhealpix_inverse(XY xy, PJ *P) { /* sphere */ + struct pj_opaque *Q = static_cast(P->opaque); + + /* Check whether (x, y) lies in the rHEALPix image. */ + if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { + LP lp; + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); + return lp; + } + xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); + return healpix_sphere_inverse(xy); +} + + +static LP e_rhealpix_inverse(XY xy, PJ *P) { /* ellipsoid */ + struct pj_opaque *Q = static_cast(P->opaque); + LP lp = {0.0,0.0}; + + /* Check whether (x, y) lies in the rHEALPix image. */ + if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y); + return lp; + } + xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); + lp = healpix_sphere_inverse(xy); + lp.phi = auth_lat(P, lp.phi, 1); + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->apa); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(healpix) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + if (P->es != 0.0) { + Q->apa = pj_authset(P->es); /* For auth_lat(). */ + if (0==Q->apa) + return destructor(P, ENOMEM); + Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ + pj_calc_ellipsoid_params (P, P->a, P->es); /* Ensure we have a consistent parameter set */ + P->fwd = e_healpix_forward; + P->inv = e_healpix_inverse; + } else { + P->fwd = s_healpix_forward; + P->inv = s_healpix_inverse; + } + + return P; +} + + +PJ *PROJECTION(rhealpix) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + Q->north_square = pj_param(P->ctx, P->params,"inorth_square").i; + Q->south_square = pj_param(P->ctx, P->params,"isouth_square").i; + + /* Check for valid north_square and south_square inputs. */ + if (Q->north_square < 0 || Q->north_square > 3) + return destructor (P, PJD_ERR_AXIS); + if (Q->south_square < 0 || Q->south_square > 3) + return destructor (P, PJD_ERR_AXIS); + if (P->es != 0.0) { + Q->apa = pj_authset(P->es); /* For auth_lat(). */ + if (0==Q->apa) + return destructor(P, ENOMEM); + Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ + P->ra = 1.0/P->a; + P->fwd = e_rhealpix_forward; + P->inv = e_rhealpix_inverse; + } else { + P->fwd = s_rhealpix_forward; + P->inv = s_rhealpix_inverse; + } + + return P; +} diff --git a/src/PJ_helmert.c b/src/PJ_helmert.c deleted file mode 100644 index 757cf950..00000000 --- a/src/PJ_helmert.c +++ /dev/null @@ -1,753 +0,0 @@ -/*********************************************************************** - - 3-, 4-and 7-parameter shifts, and their 6-, 8- - and 14-parameter kinematic counterparts. - - Thomas Knudsen, 2016-05-24 - -************************************************************************ - - Implements 3(6)-, 4(8) and 7(14)-parameter Helmert transformations for - 3D data. - - Also incorporates Molodensky-Badekas variant of 7-parameter Helmert - transformation, where the rotation is not applied regarding the centre - of the spheroid, but given a reference point. - - Primarily useful for implementation of datum shifts in transformation - pipelines. - -************************************************************************ - -Thomas Knudsen, thokn@sdfe.dk, 2016-05-24/06-05 -Kristian Evers, kreve@sdfe.dk, 2017-05-01 -Even Rouault, even.roault@spatialys.com -Last update: 2018-10-26 - -************************************************************************ -* Copyright (c) 2016, Thomas Knudsen / SDFE -* -* 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. -* -***********************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "projects.h" -#include "geocent.h" - -PROJ_HEAD(helmert, "3(6)-, 4(8)- and 7(14)-parameter Helmert shift"); -PROJ_HEAD(molobadekas, "Molodensky-Badekas transformation"); - -static XYZ helmert_forward_3d (LPZ lpz, PJ *P); -static LPZ helmert_reverse_3d (XYZ xyz, PJ *P); - - - -/***********************************************************************/ -struct pj_opaque_helmert { -/************************************************************************ - Projection specific elements for the "helmert" PJ object -************************************************************************/ - XYZ xyz; - XYZ xyz_0; - XYZ dxyz; - XYZ refp; - PJ_OPK opk; - PJ_OPK opk_0; - PJ_OPK dopk; - double scale; - double scale_0; - double dscale; - double theta; - double theta_0; - double dtheta; - double R[3][3]; - double t_epoch, t_obs; - int no_rotation, exact, fourparam; - int is_position_vector; /* 1 = position_vector, 0 = coordinate_frame */ -}; - - -/* Make the maths of the rotation operations somewhat more readable and textbook like */ -#define R00 (Q->R[0][0]) -#define R01 (Q->R[0][1]) -#define R02 (Q->R[0][2]) - -#define R10 (Q->R[1][0]) -#define R11 (Q->R[1][1]) -#define R12 (Q->R[1][2]) - -#define R20 (Q->R[2][0]) -#define R21 (Q->R[2][1]) -#define R22 (Q->R[2][2]) - -/**************************************************************************/ -static void update_parameters(PJ *P) { -/*************************************************************************** - - Update transformation parameters. - --------------------------------- - - The 14-parameter Helmert transformation is at it's core the same as the - 7-parameter transformation, since the transformation parameters are - projected forward or backwards in time via the rate of changes of the - parameters. The transformation parameters are calculated for a specific - epoch before the actual Helmert transformation is carried out. - - The transformation parameters are updated with the following equation [0]: - - . - P(t) = P(EPOCH) + P * (t - EPOCH) - - . - where EPOCH is the epoch indicated in the above table and P is the rate - of that parameter. - - [0] http://itrf.ign.fr/doc_ITRF/Transfo-ITRF2008_ITRFs.txt - -*******************************************************************************/ - - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - double dt = Q->t_obs - Q->t_epoch; - - Q->xyz.x = Q->xyz_0.x + Q->dxyz.x * dt; - Q->xyz.y = Q->xyz_0.y + Q->dxyz.y * dt; - Q->xyz.z = Q->xyz_0.z + Q->dxyz.z * dt; - - Q->opk.o = Q->opk_0.o + Q->dopk.o * dt; - Q->opk.p = Q->opk_0.p + Q->dopk.p * dt; - Q->opk.k = Q->opk_0.k + Q->dopk.k * dt; - - Q->scale = Q->scale_0 + Q->dscale * dt; - - Q->theta = Q->theta_0 + Q->dtheta * dt; - - /* debugging output */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { - proj_log_trace(P, "Transformation parameters for observation " - "t_obs=%g (t_epoch=%g):", Q->t_obs, Q->t_epoch); - proj_log_trace(P, "x: %g", Q->xyz.x); - proj_log_trace(P, "y: %g", Q->xyz.y); - proj_log_trace(P, "z: %g", Q->xyz.z); - proj_log_trace(P, "s: %g", Q->scale*1e-6); - proj_log_trace(P, "rx: %g", Q->opk.o); - proj_log_trace(P, "ry: %g", Q->opk.p); - proj_log_trace(P, "rz: %g", Q->opk.k); - proj_log_trace(P, "theta: %g", Q->theta); - } -} - -/**************************************************************************/ -static void build_rot_matrix(PJ *P) { -/*************************************************************************** - - Build rotation matrix. - ---------------------- - - Here we rename rotation indices from omega, phi, kappa (opk), to - fi (i.e. phi), theta, psi (ftp), in order to reduce the mental agility - needed to implement the expression for the rotation matrix derived over - at https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions - The relevant section is Euler angles ( z-’-x" intrinsic) -> Rotation matrix - - By default small angle approximations are used: - The matrix elements are approximated by expanding the trigonometric - functions to linear order (i.e. cos(x) = 1, sin(x) = x), and discarding - products of second order. - - This was a useful hack when calculating by hand was the only option, - but in general, today, should be avoided because: - - 1. It does not save much computation time, as the rotation matrix - is built only once and probably used many times (except when - transforming spatio-temporal coordinates). - - 2. The error induced may be too large for ultra high accuracy - applications: the Earth is huge and the linear error is - approximately the angular error multiplied by the Earth radius. - - However, in many cases the approximation is necessary, since it has - been used historically: Rotation angles from older published datum - shifts may actually be a least squares fit to the linearized rotation - approximation, hence not being strictly valid for deriving the exact - rotation matrix. In fact, most publicly available transformation - parameters are based on the approximate Helmert transform, which is why - we use that as the default setting, even though it is more correct to - use the exact form of the equations. - - So in order to fit historically derived coordinates, the access to - the approximate rotation matrix is necessary - at least in principle. - - Also, when using any published datum transformation information, one - should always check which convention (exact or approximate rotation - matrix) is expected, and whether the induced error for selecting - the opposite convention is acceptable (which it often is). - - - Sign conventions - ---------------- - - Take care: Two different sign conventions exist for the rotation terms. - - Conceptually they relate to whether we rotate the coordinate system - or the "position vector" (the vector going from the coordinate system - origin to the point being transformed, i.e. the point coordinates - interpreted as vector coordinates). - - Switching between the "position vector" and "coordinate system" - conventions is simply a matter of switching the sign of the rotation - angles, which algebraically also translates into a transposition of - the rotation matrix. - - Hence, as geodetic constants should preferably be referred to exactly - as published, the "convention" option provides the ability to switch - between the conventions. - -***************************************************************************/ - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - - double f, t, p; /* phi/fi , theta, psi */ - double cf, ct, cp; /* cos (fi, theta, psi) */ - double sf, st, sp; /* sin (fi, theta, psi) */ - - /* rename (omega, phi, kappa) to (fi, theta, psi) */ - f = Q->opk.o; - t = Q->opk.p; - p = Q->opk.k; - - /* Those equations are given assuming coordinate frame convention. */ - /* For the position vector convention, we transpose the matrix just after. */ - if (Q->exact) { - cf = cos(f); - sf = sin(f); - ct = cos(t); - st = sin(t); - cp = cos(p); - sp = sin(p); - - - R00 = ct*cp; - R01 = cf*sp + sf*st*cp; - R02 = sf*sp - cf*st*cp; - - R10 = -ct*sp; - R11 = cf*cp - sf*st*sp; - R12 = sf*cp + cf*st*sp; - - R20 = st; - R21 = -sf*ct; - R22 = cf*ct; - } else{ - R00 = 1; - R01 = p; - R02 = -t; - - R10 = -p; - R11 = 1; - R12 = f; - - R20 = t; - R21 = -f; - R22 = 1; - } - - - /* - For comparison: Description from Engsager/Poder implementation - in set_dtm_1.c (trlib) - - DATUM SHIFT: - TO = scale * ROTZ * ROTY * ROTX * FROM + TRANSLA - - ( cz sz 0) (cy 0 -sy) (1 0 0) - ROTZ=(-sz cz 0), ROTY=(0 1 0), ROTX=(0 cx sx) - ( 0 0 1) (sy 0 cy) (0 -sx cx) - - trp->r11 = cos_ry*cos_rz; - trp->r12 = cos_rx*sin_rz + sin_rx*sin_ry*cos_rz; - trp->r13 = sin_rx*sin_rz - cos_rx*sin_ry*cos_rz; - - trp->r21 = -cos_ry*sin_rz; - trp->r22 = cos_rx*cos_rz - sin_rx*sin_ry*sin_rz; - trp->r23 = sin_rx*cos_rz + cos_rx*sin_ry*sin_rz; - - trp->r31 = sin_ry; - trp->r32 = -sin_rx*cos_ry; - trp->r33 = cos_rx*cos_ry; - - trp->scale = 1.0 + scale; - */ - - - if (Q->is_position_vector) { - double r; - r = R01; R01 = R10; R10 = r; - r = R02; R02 = R20; R20 = r; - r = R12; R12 = R21; R21 = r; - } - - /* some debugging output */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { - proj_log_trace(P, "Rotation Matrix:"); - proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R00, R01, R02); - proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R10, R11, R12); - proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R20, R21, R22); - } -} - - - - -/***********************************************************************/ -static XY helmert_forward (LP lp, PJ *P) { -/***********************************************************************/ - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - double x, y, cr, sr; - point.lp = lp; - - cr = cos(Q->theta) * Q->scale; - sr = sin(Q->theta) * Q->scale; - x = point.xy.x; - y = point.xy.y; - - point.xy.x = cr*x + sr*y + Q->xyz_0.x; - point.xy.y = -sr*x + cr*y + Q->xyz_0.y; - - return point.xy; -} - - -/***********************************************************************/ -static LP helmert_reverse (XY xy, PJ *P) { -/***********************************************************************/ - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - double x, y, sr, cr; - point.xy = xy; - - cr = cos(Q->theta) / Q->scale; - sr = sin(Q->theta) / Q->scale; - x = point.xy.x - Q->xyz_0.x; - y = point.xy.y - Q->xyz_0.y; - - point.xy.x = x*cr - y*sr; - point.xy.y = x*sr + y*cr; - - return point.lp; -} - - -/***********************************************************************/ -static XYZ helmert_forward_3d (LPZ lpz, PJ *P) { -/***********************************************************************/ - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - double X, Y, Z, scale; - - point.lpz = lpz; - - if (Q->fourparam) { - point.xy = helmert_forward(point.lp, P); - return point.xyz; - } - - if (Q->no_rotation) { - point.xyz.x = lpz.lam + Q->xyz.x; - point.xyz.y = lpz.phi + Q->xyz.y; - point.xyz.z = lpz.z + Q->xyz.z; - return point.xyz; - } - - scale = 1 + Q->scale * 1e-6; - - X = lpz.lam - Q->refp.x; - Y = lpz.phi - Q->refp.y; - Z = lpz.z - Q->refp.z; - - - point.xyz.x = scale * ( R00 * X + R01 * Y + R02 * Z); - point.xyz.y = scale * ( R10 * X + R11 * Y + R12 * Z); - point.xyz.z = scale * ( R20 * X + R21 * Y + R22 * Z); - - point.xyz.x += Q->xyz.x; /* for Molodensky-Badekas, Q->xyz already incorporates the Q->refp offset */ - point.xyz.y += Q->xyz.y; - point.xyz.z += Q->xyz.z; - - return point.xyz; -} - - -/***********************************************************************/ -static LPZ helmert_reverse_3d (XYZ xyz, PJ *P) { -/***********************************************************************/ - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - double X, Y, Z, scale; - - point.xyz = xyz; - - if (Q->fourparam) { - point.lp = helmert_reverse(point.xy, P); - return point.lpz; - } - - if (Q->no_rotation) { - point.xyz.x = xyz.x - Q->xyz.x; - point.xyz.y = xyz.y - Q->xyz.y; - point.xyz.z = xyz.z - Q->xyz.z; - return point.lpz; - } - - scale = 1 + Q->scale * 1e-6; - - /* Unscale and deoffset */ - X = (xyz.x - Q->xyz.x) / scale; - Y = (xyz.y - Q->xyz.y) / scale; - Z = (xyz.z - Q->xyz.z) / scale; - - /* Inverse rotation through transpose multiplication */ - point.xyz.x = ( R00 * X + R10 * Y + R20 * Z) + Q->refp.x; - point.xyz.y = ( R01 * X + R11 * Y + R21 * Z) + Q->refp.y; - point.xyz.z = ( R02 * X + R12 * Y + R22 * Z) + Q->refp.z; - - return point.lpz; -} - - -static PJ_COORD helmert_forward_4d (PJ_COORD point, PJ *P) { - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - - /* We only need to rebuild the rotation matrix if the - * observation time is different from the last call */ - double t_obs = (point.xyzt.t == HUGE_VAL) ? Q->t_epoch : point.xyzt.t; - if (t_obs != Q->t_obs) { - Q->t_obs = t_obs; - update_parameters(P); - build_rot_matrix(P); - } - - point.xyz = helmert_forward_3d (point.lpz, P); - - return point; -} - - -static PJ_COORD helmert_reverse_4d (PJ_COORD point, PJ *P) { - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; - - /* We only need to rebuild the rotation matrix if the - * observation time is different from the last call */ - double t_obs = (point.xyzt.t == HUGE_VAL) ? Q->t_epoch : point.xyzt.t; - if (t_obs != Q->t_obs) { - Q->t_obs = t_obs; - update_parameters(P); - build_rot_matrix(P); - } - - point.lpz = helmert_reverse_3d (point.xyz, P); - - return point; -} - -/* Arcsecond to radians */ -#define ARCSEC_TO_RAD (DEG_TO_RAD / 3600.0) - - -static PJ* init_helmert_six_parameters(PJ* P) { - struct pj_opaque_helmert *Q = pj_calloc (1, sizeof (struct pj_opaque_helmert)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = (void *) Q; - - /* In most cases, we work on 3D cartesian coordinates */ - P->left = PJ_IO_UNITS_CARTESIAN; - P->right = PJ_IO_UNITS_CARTESIAN; - - /* Translations */ - if (pj_param (P->ctx, P->params, "tx").i) - Q->xyz_0.x = pj_param (P->ctx, P->params, "dx").f; - - if (pj_param (P->ctx, P->params, "ty").i) - Q->xyz_0.y = pj_param (P->ctx, P->params, "dy").f; - - if (pj_param (P->ctx, P->params, "tz").i) - Q->xyz_0.z = pj_param (P->ctx, P->params, "dz").f; - - /* Rotations */ - if (pj_param (P->ctx, P->params, "trx").i) - Q->opk_0.o = pj_param (P->ctx, P->params, "drx").f * ARCSEC_TO_RAD; - - if (pj_param (P->ctx, P->params, "try").i) - Q->opk_0.p = pj_param (P->ctx, P->params, "dry").f * ARCSEC_TO_RAD; - - if (pj_param (P->ctx, P->params, "trz").i) - Q->opk_0.k = pj_param (P->ctx, P->params, "drz").f * ARCSEC_TO_RAD; - - /* Use small angle approximations? */ - if (pj_param (P->ctx, P->params, "bexact").i) - Q->exact = 1; - - return P; -} - - -static PJ* read_convention(PJ* P) { - - struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *)P->opaque; - - /* In case there are rotational terms, we require an explicit convention - * to be provided. */ - if (!Q->no_rotation) { - const char* convention = pj_param (P->ctx, P->params, "sconvention").s; - if( !convention ) { - proj_log_error (P, "helmert: missing 'convention' argument"); - return pj_default_destructor (P, PJD_ERR_MISSING_ARGS); - } - if( strcmp(convention, "position_vector") == 0 ) { - Q->is_position_vector = 1; - } - else if( strcmp(convention, "coordinate_frame") == 0 ) { - Q->is_position_vector = 0; - } - else { - proj_log_error (P, "helmert: invalid value for 'convention' argument"); - return pj_default_destructor (P, PJD_ERR_INVALID_ARG); - } - - /* historically towgs84 in PROJ has always been using position_vector - * convention. Accepting coordinate_frame would be confusing. */ - if (pj_param_exists (P->params, "towgs84")) { - if( !Q->is_position_vector ) { - proj_log_error (P, "helmert: towgs84 should only be used with " - "convention=position_vector"); - return pj_default_destructor (P, PJD_ERR_INVALID_ARG); - } - } - } - - return P; -} - - -/***********************************************************************/ -PJ *TRANSFORMATION(helmert, 0) { -/***********************************************************************/ - - struct pj_opaque_helmert *Q; - - if( !init_helmert_six_parameters(P) ) { - return 0; - } - - /* In the 2D case, the coordinates are projected */ - if (pj_param_exists (P->params, "theta")) { - P->left = PJ_IO_UNITS_PROJECTED; - P->right = PJ_IO_UNITS_PROJECTED; - } - - P->fwd4d = helmert_forward_4d; - P->inv4d = helmert_reverse_4d; - P->fwd3d = helmert_forward_3d; - P->inv3d = helmert_reverse_3d; - P->fwd = helmert_forward; - P->inv = helmert_reverse; - - Q = (struct pj_opaque_helmert *)P->opaque; - - /* Detect obsolete transpose flag and error out if found */ - if (pj_param (P->ctx, P->params, "ttranspose").i) { - proj_log_error (P, "helmert: 'transpose' argument is no longer valid. " - "Use convention=position_vector/coordinate_frame"); - return pj_default_destructor (P, PJD_ERR_INVALID_ARG); - } - - /* Support the classic PROJ towgs84 parameter, but allow later overrides.*/ - /* Note that if towgs84 is specified, the datum_params array is set up */ - /* for us automagically by the pj_datum_set call in pj_init_ctx */ - if (pj_param_exists (P->params, "towgs84")) { - Q->xyz_0.x = P->datum_params[0]; - Q->xyz_0.y = P->datum_params[1]; - Q->xyz_0.z = P->datum_params[2]; - - Q->opk_0.o = P->datum_params[3]; - Q->opk_0.p = P->datum_params[4]; - Q->opk_0.k = P->datum_params[5]; - - /* We must undo conversion to absolute scale from pj_datum_set */ - if (0==P->datum_params[6]) - Q->scale_0 = 0; - else - Q->scale_0 = (P->datum_params[6] - 1) * 1e6; - } - - if (pj_param (P->ctx, P->params, "ttheta").i) { - Q->theta_0 = pj_param (P->ctx, P->params, "dtheta").f * ARCSEC_TO_RAD; - Q->fourparam = 1; - Q->scale_0 = 1.0; /* default scale for the 4-param shift */ - } - - /* Scale */ - if (pj_param (P->ctx, P->params, "ts").i) { - Q->scale_0 = pj_param (P->ctx, P->params, "ds").f; - if (pj_param (P->ctx, P->params, "ttheta").i && Q->scale_0 == 0.0) - return pj_default_destructor (P, PJD_ERR_INVALID_SCALE); - } - - /* Translation rates */ - if (pj_param(P->ctx, P->params, "tdx").i) - Q->dxyz.x = pj_param (P->ctx, P->params, "ddx").f; - - if (pj_param(P->ctx, P->params, "tdy").i) - Q->dxyz.y = pj_param (P->ctx, P->params, "ddy").f; - - if (pj_param(P->ctx, P->params, "tdz").i) - Q->dxyz.z = pj_param (P->ctx, P->params, "ddz").f; - - /* Rotations rates */ - if (pj_param (P->ctx, P->params, "tdrx").i) - Q->dopk.o = pj_param (P->ctx, P->params, "ddrx").f * ARCSEC_TO_RAD; - - if (pj_param (P->ctx, P->params, "tdry").i) - Q->dopk.p = pj_param (P->ctx, P->params, "ddry").f * ARCSEC_TO_RAD; - - if (pj_param (P->ctx, P->params, "tdrz").i) - Q->dopk.k = pj_param (P->ctx, P->params, "ddrz").f * ARCSEC_TO_RAD; - - if (pj_param (P->ctx, P->params, "tdtheta").i) - Q->dtheta = pj_param (P->ctx, P->params, "ddtheta").f * ARCSEC_TO_RAD; - - /* Scale rate */ - if (pj_param (P->ctx, P->params, "tds").i) - Q->dscale = pj_param (P->ctx, P->params, "dds").f; - - - /* Epoch */ - if (pj_param(P->ctx, P->params, "tt_epoch").i) - Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; - - if (pj_param(P->ctx, P->params, "tt_obs").i) - Q->t_obs = pj_param (P->ctx, P->params, "dt_obs").f; - - Q->xyz = Q->xyz_0; - Q->opk = Q->opk_0; - Q->scale = Q->scale_0; - Q->theta = Q->theta_0; - - if ((Q->opk.o==0) && (Q->opk.p==0) && (Q->opk.k==0) && (Q->scale==0) && - (Q->dopk.o==0) && (Q->dopk.p==0) && (Q->dopk.k==0)) { - Q->no_rotation = 1; - } - - if( !read_convention(P) ) { - return 0; - } - - /* Let's help with debugging */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - proj_log_debug(P, "Helmert parameters:"); - proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); - proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", - Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); - proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, - Q->no_rotation ? "" : - Q->is_position_vector ? " convention=position_vector" : - " convention=coordinate_frame"); - proj_log_debug(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); - proj_log_debug(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); - proj_log_debug(P, "ds= %8.5f t_epoch=%8.5f t_obs=%8.5f", Q->dscale, Q->t_epoch, Q->t_obs); - } - - if (Q->no_rotation) { - return P; - } - - update_parameters(P); - build_rot_matrix(P); - - return P; -} - - -/***********************************************************************/ -PJ *TRANSFORMATION(molobadekas, 0) { -/***********************************************************************/ - - struct pj_opaque_helmert *Q; - - if( !init_helmert_six_parameters(P) ) { - return 0; - } - - P->fwd3d = helmert_forward_3d; - P->inv3d = helmert_reverse_3d; - - Q = (struct pj_opaque_helmert *)P->opaque; - - /* Scale */ - if (pj_param (P->ctx, P->params, "ts").i) { - Q->scale_0 = pj_param (P->ctx, P->params, "ds").f; - } - - Q->opk = Q->opk_0; - Q->scale = Q->scale_0; - - if( !read_convention(P) ) { - return 0; - } - - /* Reference point */ - if (pj_param (P->ctx, P->params, "tpx").i) - Q->refp.x = pj_param (P->ctx, P->params, "dpx").f; - - if (pj_param (P->ctx, P->params, "tpy").i) - Q->refp.y = pj_param (P->ctx, P->params, "dpy").f; - - if (pj_param (P->ctx, P->params, "tpz").i) - Q->refp.z = pj_param (P->ctx, P->params, "dpz").f; - - - /* Let's help with debugging */ - if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { - proj_log_debug(P, "Molodensky-Badekas parameters:"); - proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z); - proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", - Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); - proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, - Q->is_position_vector ? " convention=position_vector" : - " convention=coordinate_frame"); - proj_log_debug(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z); - } - - /* as an optimization, we incorporate the refp in the translation terms */ - Q->xyz_0.x += Q->refp.x; - Q->xyz_0.y += Q->refp.y; - Q->xyz_0.z += Q->refp.z; - - Q->xyz = Q->xyz_0; - - build_rot_matrix(P); - - return P; -} diff --git a/src/PJ_helmert.cpp b/src/PJ_helmert.cpp new file mode 100644 index 00000000..c19422cb --- /dev/null +++ b/src/PJ_helmert.cpp @@ -0,0 +1,753 @@ +/*********************************************************************** + + 3-, 4-and 7-parameter shifts, and their 6-, 8- + and 14-parameter kinematic counterparts. + + Thomas Knudsen, 2016-05-24 + +************************************************************************ + + Implements 3(6)-, 4(8) and 7(14)-parameter Helmert transformations for + 3D data. + + Also incorporates Molodensky-Badekas variant of 7-parameter Helmert + transformation, where the rotation is not applied regarding the centre + of the spheroid, but given a reference point. + + Primarily useful for implementation of datum shifts in transformation + pipelines. + +************************************************************************ + +Thomas Knudsen, thokn@sdfe.dk, 2016-05-24/06-05 +Kristian Evers, kreve@sdfe.dk, 2017-05-01 +Even Rouault, even.roault@spatialys.com +Last update: 2018-10-26 + +************************************************************************ +* Copyright (c) 2016, Thomas Knudsen / SDFE +* +* 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. +* +***********************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "projects.h" +#include "geocent.h" + +PROJ_HEAD(helmert, "3(6)-, 4(8)- and 7(14)-parameter Helmert shift"); +PROJ_HEAD(molobadekas, "Molodensky-Badekas transformation"); + +static XYZ helmert_forward_3d (LPZ lpz, PJ *P); +static LPZ helmert_reverse_3d (XYZ xyz, PJ *P); + + + +/***********************************************************************/ +struct pj_opaque_helmert { +/************************************************************************ + Projection specific elements for the "helmert" PJ object +************************************************************************/ + XYZ xyz; + XYZ xyz_0; + XYZ dxyz; + XYZ refp; + PJ_OPK opk; + PJ_OPK opk_0; + PJ_OPK dopk; + double scale; + double scale_0; + double dscale; + double theta; + double theta_0; + double dtheta; + double R[3][3]; + double t_epoch, t_obs; + int no_rotation, exact, fourparam; + int is_position_vector; /* 1 = position_vector, 0 = coordinate_frame */ +}; + + +/* Make the maths of the rotation operations somewhat more readable and textbook like */ +#define R00 (Q->R[0][0]) +#define R01 (Q->R[0][1]) +#define R02 (Q->R[0][2]) + +#define R10 (Q->R[1][0]) +#define R11 (Q->R[1][1]) +#define R12 (Q->R[1][2]) + +#define R20 (Q->R[2][0]) +#define R21 (Q->R[2][1]) +#define R22 (Q->R[2][2]) + +/**************************************************************************/ +static void update_parameters(PJ *P) { +/*************************************************************************** + + Update transformation parameters. + --------------------------------- + + The 14-parameter Helmert transformation is at it's core the same as the + 7-parameter transformation, since the transformation parameters are + projected forward or backwards in time via the rate of changes of the + parameters. The transformation parameters are calculated for a specific + epoch before the actual Helmert transformation is carried out. + + The transformation parameters are updated with the following equation [0]: + + . + P(t) = P(EPOCH) + P * (t - EPOCH) + + . + where EPOCH is the epoch indicated in the above table and P is the rate + of that parameter. + + [0] http://itrf.ign.fr/doc_ITRF/Transfo-ITRF2008_ITRFs.txt + +*******************************************************************************/ + + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + double dt = Q->t_obs - Q->t_epoch; + + Q->xyz.x = Q->xyz_0.x + Q->dxyz.x * dt; + Q->xyz.y = Q->xyz_0.y + Q->dxyz.y * dt; + Q->xyz.z = Q->xyz_0.z + Q->dxyz.z * dt; + + Q->opk.o = Q->opk_0.o + Q->dopk.o * dt; + Q->opk.p = Q->opk_0.p + Q->dopk.p * dt; + Q->opk.k = Q->opk_0.k + Q->dopk.k * dt; + + Q->scale = Q->scale_0 + Q->dscale * dt; + + Q->theta = Q->theta_0 + Q->dtheta * dt; + + /* debugging output */ + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { + proj_log_trace(P, "Transformation parameters for observation " + "t_obs=%g (t_epoch=%g):", Q->t_obs, Q->t_epoch); + proj_log_trace(P, "x: %g", Q->xyz.x); + proj_log_trace(P, "y: %g", Q->xyz.y); + proj_log_trace(P, "z: %g", Q->xyz.z); + proj_log_trace(P, "s: %g", Q->scale*1e-6); + proj_log_trace(P, "rx: %g", Q->opk.o); + proj_log_trace(P, "ry: %g", Q->opk.p); + proj_log_trace(P, "rz: %g", Q->opk.k); + proj_log_trace(P, "theta: %g", Q->theta); + } +} + +/**************************************************************************/ +static void build_rot_matrix(PJ *P) { +/*************************************************************************** + + Build rotation matrix. + ---------------------- + + Here we rename rotation indices from omega, phi, kappa (opk), to + fi (i.e. phi), theta, psi (ftp), in order to reduce the mental agility + needed to implement the expression for the rotation matrix derived over + at https://en.wikipedia.org/wiki/Rotation_formalisms_in_three_dimensions + The relevant section is Euler angles ( z-’-x" intrinsic) -> Rotation matrix + + By default small angle approximations are used: + The matrix elements are approximated by expanding the trigonometric + functions to linear order (i.e. cos(x) = 1, sin(x) = x), and discarding + products of second order. + + This was a useful hack when calculating by hand was the only option, + but in general, today, should be avoided because: + + 1. It does not save much computation time, as the rotation matrix + is built only once and probably used many times (except when + transforming spatio-temporal coordinates). + + 2. The error induced may be too large for ultra high accuracy + applications: the Earth is huge and the linear error is + approximately the angular error multiplied by the Earth radius. + + However, in many cases the approximation is necessary, since it has + been used historically: Rotation angles from older published datum + shifts may actually be a least squares fit to the linearized rotation + approximation, hence not being strictly valid for deriving the exact + rotation matrix. In fact, most publicly available transformation + parameters are based on the approximate Helmert transform, which is why + we use that as the default setting, even though it is more correct to + use the exact form of the equations. + + So in order to fit historically derived coordinates, the access to + the approximate rotation matrix is necessary - at least in principle. + + Also, when using any published datum transformation information, one + should always check which convention (exact or approximate rotation + matrix) is expected, and whether the induced error for selecting + the opposite convention is acceptable (which it often is). + + + Sign conventions + ---------------- + + Take care: Two different sign conventions exist for the rotation terms. + + Conceptually they relate to whether we rotate the coordinate system + or the "position vector" (the vector going from the coordinate system + origin to the point being transformed, i.e. the point coordinates + interpreted as vector coordinates). + + Switching between the "position vector" and "coordinate system" + conventions is simply a matter of switching the sign of the rotation + angles, which algebraically also translates into a transposition of + the rotation matrix. + + Hence, as geodetic constants should preferably be referred to exactly + as published, the "convention" option provides the ability to switch + between the conventions. + +***************************************************************************/ + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + + double f, t, p; /* phi/fi , theta, psi */ + double cf, ct, cp; /* cos (fi, theta, psi) */ + double sf, st, sp; /* sin (fi, theta, psi) */ + + /* rename (omega, phi, kappa) to (fi, theta, psi) */ + f = Q->opk.o; + t = Q->opk.p; + p = Q->opk.k; + + /* Those equations are given assuming coordinate frame convention. */ + /* For the position vector convention, we transpose the matrix just after. */ + if (Q->exact) { + cf = cos(f); + sf = sin(f); + ct = cos(t); + st = sin(t); + cp = cos(p); + sp = sin(p); + + + R00 = ct*cp; + R01 = cf*sp + sf*st*cp; + R02 = sf*sp - cf*st*cp; + + R10 = -ct*sp; + R11 = cf*cp - sf*st*sp; + R12 = sf*cp + cf*st*sp; + + R20 = st; + R21 = -sf*ct; + R22 = cf*ct; + } else{ + R00 = 1; + R01 = p; + R02 = -t; + + R10 = -p; + R11 = 1; + R12 = f; + + R20 = t; + R21 = -f; + R22 = 1; + } + + + /* + For comparison: Description from Engsager/Poder implementation + in set_dtm_1.c (trlib) + + DATUM SHIFT: + TO = scale * ROTZ * ROTY * ROTX * FROM + TRANSLA + + ( cz sz 0) (cy 0 -sy) (1 0 0) + ROTZ=(-sz cz 0), ROTY=(0 1 0), ROTX=(0 cx sx) + ( 0 0 1) (sy 0 cy) (0 -sx cx) + + trp->r11 = cos_ry*cos_rz; + trp->r12 = cos_rx*sin_rz + sin_rx*sin_ry*cos_rz; + trp->r13 = sin_rx*sin_rz - cos_rx*sin_ry*cos_rz; + + trp->r21 = -cos_ry*sin_rz; + trp->r22 = cos_rx*cos_rz - sin_rx*sin_ry*sin_rz; + trp->r23 = sin_rx*cos_rz + cos_rx*sin_ry*sin_rz; + + trp->r31 = sin_ry; + trp->r32 = -sin_rx*cos_ry; + trp->r33 = cos_rx*cos_ry; + + trp->scale = 1.0 + scale; + */ + + + if (Q->is_position_vector) { + double r; + r = R01; R01 = R10; R10 = r; + r = R02; R02 = R20; R20 = r; + r = R12; R12 = R21; R21 = r; + } + + /* some debugging output */ + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_TRACE) { + proj_log_trace(P, "Rotation Matrix:"); + proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R00, R01, R02); + proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R10, R11, R12); + proj_log_trace(P, " | % 6.6g % 6.6g % 6.6g |", R20, R21, R22); + } +} + + + + +/***********************************************************************/ +static XY helmert_forward (LP lp, PJ *P) { +/***********************************************************************/ + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + double x, y, cr, sr; + point.lp = lp; + + cr = cos(Q->theta) * Q->scale; + sr = sin(Q->theta) * Q->scale; + x = point.xy.x; + y = point.xy.y; + + point.xy.x = cr*x + sr*y + Q->xyz_0.x; + point.xy.y = -sr*x + cr*y + Q->xyz_0.y; + + return point.xy; +} + + +/***********************************************************************/ +static LP helmert_reverse (XY xy, PJ *P) { +/***********************************************************************/ + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + double x, y, sr, cr; + point.xy = xy; + + cr = cos(Q->theta) / Q->scale; + sr = sin(Q->theta) / Q->scale; + x = point.xy.x - Q->xyz_0.x; + y = point.xy.y - Q->xyz_0.y; + + point.xy.x = x*cr - y*sr; + point.xy.y = x*sr + y*cr; + + return point.lp; +} + + +/***********************************************************************/ +static XYZ helmert_forward_3d (LPZ lpz, PJ *P) { +/***********************************************************************/ + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + double X, Y, Z, scale; + + point.lpz = lpz; + + if (Q->fourparam) { + point.xy = helmert_forward(point.lp, P); + return point.xyz; + } + + if (Q->no_rotation) { + point.xyz.x = lpz.lam + Q->xyz.x; + point.xyz.y = lpz.phi + Q->xyz.y; + point.xyz.z = lpz.z + Q->xyz.z; + return point.xyz; + } + + scale = 1 + Q->scale * 1e-6; + + X = lpz.lam - Q->refp.x; + Y = lpz.phi - Q->refp.y; + Z = lpz.z - Q->refp.z; + + + point.xyz.x = scale * ( R00 * X + R01 * Y + R02 * Z); + point.xyz.y = scale * ( R10 * X + R11 * Y + R12 * Z); + point.xyz.z = scale * ( R20 * X + R21 * Y + R22 * Z); + + point.xyz.x += Q->xyz.x; /* for Molodensky-Badekas, Q->xyz already incorporates the Q->refp offset */ + point.xyz.y += Q->xyz.y; + point.xyz.z += Q->xyz.z; + + return point.xyz; +} + + +/***********************************************************************/ +static LPZ helmert_reverse_3d (XYZ xyz, PJ *P) { +/***********************************************************************/ + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + double X, Y, Z, scale; + + point.xyz = xyz; + + if (Q->fourparam) { + point.lp = helmert_reverse(point.xy, P); + return point.lpz; + } + + if (Q->no_rotation) { + point.xyz.x = xyz.x - Q->xyz.x; + point.xyz.y = xyz.y - Q->xyz.y; + point.xyz.z = xyz.z - Q->xyz.z; + return point.lpz; + } + + scale = 1 + Q->scale * 1e-6; + + /* Unscale and deoffset */ + X = (xyz.x - Q->xyz.x) / scale; + Y = (xyz.y - Q->xyz.y) / scale; + Z = (xyz.z - Q->xyz.z) / scale; + + /* Inverse rotation through transpose multiplication */ + point.xyz.x = ( R00 * X + R10 * Y + R20 * Z) + Q->refp.x; + point.xyz.y = ( R01 * X + R11 * Y + R21 * Z) + Q->refp.y; + point.xyz.z = ( R02 * X + R12 * Y + R22 * Z) + Q->refp.z; + + return point.lpz; +} + + +static PJ_COORD helmert_forward_4d (PJ_COORD point, PJ *P) { + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + + /* We only need to rebuild the rotation matrix if the + * observation time is different from the last call */ + double t_obs = (point.xyzt.t == HUGE_VAL) ? Q->t_epoch : point.xyzt.t; + if (t_obs != Q->t_obs) { + Q->t_obs = t_obs; + update_parameters(P); + build_rot_matrix(P); + } + + point.xyz = helmert_forward_3d (point.lpz, P); + + return point; +} + + +static PJ_COORD helmert_reverse_4d (PJ_COORD point, PJ *P) { + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *) P->opaque; + + /* We only need to rebuild the rotation matrix if the + * observation time is different from the last call */ + double t_obs = (point.xyzt.t == HUGE_VAL) ? Q->t_epoch : point.xyzt.t; + if (t_obs != Q->t_obs) { + Q->t_obs = t_obs; + update_parameters(P); + build_rot_matrix(P); + } + + point.lpz = helmert_reverse_3d (point.xyz, P); + + return point; +} + +/* Arcsecond to radians */ +#define ARCSEC_TO_RAD (DEG_TO_RAD / 3600.0) + + +static PJ* init_helmert_six_parameters(PJ* P) { + struct pj_opaque_helmert *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque_helmert))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = (void *) Q; + + /* In most cases, we work on 3D cartesian coordinates */ + P->left = PJ_IO_UNITS_CARTESIAN; + P->right = PJ_IO_UNITS_CARTESIAN; + + /* Translations */ + if (pj_param (P->ctx, P->params, "tx").i) + Q->xyz_0.x = pj_param (P->ctx, P->params, "dx").f; + + if (pj_param (P->ctx, P->params, "ty").i) + Q->xyz_0.y = pj_param (P->ctx, P->params, "dy").f; + + if (pj_param (P->ctx, P->params, "tz").i) + Q->xyz_0.z = pj_param (P->ctx, P->params, "dz").f; + + /* Rotations */ + if (pj_param (P->ctx, P->params, "trx").i) + Q->opk_0.o = pj_param (P->ctx, P->params, "drx").f * ARCSEC_TO_RAD; + + if (pj_param (P->ctx, P->params, "try").i) + Q->opk_0.p = pj_param (P->ctx, P->params, "dry").f * ARCSEC_TO_RAD; + + if (pj_param (P->ctx, P->params, "trz").i) + Q->opk_0.k = pj_param (P->ctx, P->params, "drz").f * ARCSEC_TO_RAD; + + /* Use small angle approximations? */ + if (pj_param (P->ctx, P->params, "bexact").i) + Q->exact = 1; + + return P; +} + + +static PJ* read_convention(PJ* P) { + + struct pj_opaque_helmert *Q = (struct pj_opaque_helmert *)P->opaque; + + /* In case there are rotational terms, we require an explicit convention + * to be provided. */ + if (!Q->no_rotation) { + const char* convention = pj_param (P->ctx, P->params, "sconvention").s; + if( !convention ) { + proj_log_error (P, "helmert: missing 'convention' argument"); + return pj_default_destructor (P, PJD_ERR_MISSING_ARGS); + } + if( strcmp(convention, "position_vector") == 0 ) { + Q->is_position_vector = 1; + } + else if( strcmp(convention, "coordinate_frame") == 0 ) { + Q->is_position_vector = 0; + } + else { + proj_log_error (P, "helmert: invalid value for 'convention' argument"); + return pj_default_destructor (P, PJD_ERR_INVALID_ARG); + } + + /* historically towgs84 in PROJ has always been using position_vector + * convention. Accepting coordinate_frame would be confusing. */ + if (pj_param_exists (P->params, "towgs84")) { + if( !Q->is_position_vector ) { + proj_log_error (P, "helmert: towgs84 should only be used with " + "convention=position_vector"); + return pj_default_destructor (P, PJD_ERR_INVALID_ARG); + } + } + } + + return P; +} + + +/***********************************************************************/ +PJ *TRANSFORMATION(helmert, 0) { +/***********************************************************************/ + + struct pj_opaque_helmert *Q; + + if( !init_helmert_six_parameters(P) ) { + return 0; + } + + /* In the 2D case, the coordinates are projected */ + if (pj_param_exists (P->params, "theta")) { + P->left = PJ_IO_UNITS_PROJECTED; + P->right = PJ_IO_UNITS_PROJECTED; + } + + P->fwd4d = helmert_forward_4d; + P->inv4d = helmert_reverse_4d; + P->fwd3d = helmert_forward_3d; + P->inv3d = helmert_reverse_3d; + P->fwd = helmert_forward; + P->inv = helmert_reverse; + + Q = (struct pj_opaque_helmert *)P->opaque; + + /* Detect obsolete transpose flag and error out if found */ + if (pj_param (P->ctx, P->params, "ttranspose").i) { + proj_log_error (P, "helmert: 'transpose' argument is no longer valid. " + "Use convention=position_vector/coordinate_frame"); + return pj_default_destructor (P, PJD_ERR_INVALID_ARG); + } + + /* Support the classic PROJ towgs84 parameter, but allow later overrides.*/ + /* Note that if towgs84 is specified, the datum_params array is set up */ + /* for us automagically by the pj_datum_set call in pj_init_ctx */ + if (pj_param_exists (P->params, "towgs84")) { + Q->xyz_0.x = P->datum_params[0]; + Q->xyz_0.y = P->datum_params[1]; + Q->xyz_0.z = P->datum_params[2]; + + Q->opk_0.o = P->datum_params[3]; + Q->opk_0.p = P->datum_params[4]; + Q->opk_0.k = P->datum_params[5]; + + /* We must undo conversion to absolute scale from pj_datum_set */ + if (0==P->datum_params[6]) + Q->scale_0 = 0; + else + Q->scale_0 = (P->datum_params[6] - 1) * 1e6; + } + + if (pj_param (P->ctx, P->params, "ttheta").i) { + Q->theta_0 = pj_param (P->ctx, P->params, "dtheta").f * ARCSEC_TO_RAD; + Q->fourparam = 1; + Q->scale_0 = 1.0; /* default scale for the 4-param shift */ + } + + /* Scale */ + if (pj_param (P->ctx, P->params, "ts").i) { + Q->scale_0 = pj_param (P->ctx, P->params, "ds").f; + if (pj_param (P->ctx, P->params, "ttheta").i && Q->scale_0 == 0.0) + return pj_default_destructor (P, PJD_ERR_INVALID_SCALE); + } + + /* Translation rates */ + if (pj_param(P->ctx, P->params, "tdx").i) + Q->dxyz.x = pj_param (P->ctx, P->params, "ddx").f; + + if (pj_param(P->ctx, P->params, "tdy").i) + Q->dxyz.y = pj_param (P->ctx, P->params, "ddy").f; + + if (pj_param(P->ctx, P->params, "tdz").i) + Q->dxyz.z = pj_param (P->ctx, P->params, "ddz").f; + + /* Rotations rates */ + if (pj_param (P->ctx, P->params, "tdrx").i) + Q->dopk.o = pj_param (P->ctx, P->params, "ddrx").f * ARCSEC_TO_RAD; + + if (pj_param (P->ctx, P->params, "tdry").i) + Q->dopk.p = pj_param (P->ctx, P->params, "ddry").f * ARCSEC_TO_RAD; + + if (pj_param (P->ctx, P->params, "tdrz").i) + Q->dopk.k = pj_param (P->ctx, P->params, "ddrz").f * ARCSEC_TO_RAD; + + if (pj_param (P->ctx, P->params, "tdtheta").i) + Q->dtheta = pj_param (P->ctx, P->params, "ddtheta").f * ARCSEC_TO_RAD; + + /* Scale rate */ + if (pj_param (P->ctx, P->params, "tds").i) + Q->dscale = pj_param (P->ctx, P->params, "dds").f; + + + /* Epoch */ + if (pj_param(P->ctx, P->params, "tt_epoch").i) + Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; + + if (pj_param(P->ctx, P->params, "tt_obs").i) + Q->t_obs = pj_param (P->ctx, P->params, "dt_obs").f; + + Q->xyz = Q->xyz_0; + Q->opk = Q->opk_0; + Q->scale = Q->scale_0; + Q->theta = Q->theta_0; + + if ((Q->opk.o==0) && (Q->opk.p==0) && (Q->opk.k==0) && (Q->scale==0) && + (Q->dopk.o==0) && (Q->dopk.p==0) && (Q->dopk.k==0)) { + Q->no_rotation = 1; + } + + if( !read_convention(P) ) { + return 0; + } + + /* Let's help with debugging */ + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + proj_log_debug(P, "Helmert parameters:"); + proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz.x, Q->xyz.y, Q->xyz.z); + proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", + Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); + proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, + Q->no_rotation ? "" : + Q->is_position_vector ? " convention=position_vector" : + " convention=coordinate_frame"); + proj_log_debug(P, "dx= %8.5f dy= %8.5f dz= %8.5f", Q->dxyz.x, Q->dxyz.y, Q->dxyz.z); + proj_log_debug(P, "drx=%8.5f dry=%8.5f drz=%8.5f", Q->dopk.o, Q->dopk.p, Q->dopk.k); + proj_log_debug(P, "ds= %8.5f t_epoch=%8.5f t_obs=%8.5f", Q->dscale, Q->t_epoch, Q->t_obs); + } + + if (Q->no_rotation) { + return P; + } + + update_parameters(P); + build_rot_matrix(P); + + return P; +} + + +/***********************************************************************/ +PJ *TRANSFORMATION(molobadekas, 0) { +/***********************************************************************/ + + struct pj_opaque_helmert *Q; + + if( !init_helmert_six_parameters(P) ) { + return 0; + } + + P->fwd3d = helmert_forward_3d; + P->inv3d = helmert_reverse_3d; + + Q = (struct pj_opaque_helmert *)P->opaque; + + /* Scale */ + if (pj_param (P->ctx, P->params, "ts").i) { + Q->scale_0 = pj_param (P->ctx, P->params, "ds").f; + } + + Q->opk = Q->opk_0; + Q->scale = Q->scale_0; + + if( !read_convention(P) ) { + return 0; + } + + /* Reference point */ + if (pj_param (P->ctx, P->params, "tpx").i) + Q->refp.x = pj_param (P->ctx, P->params, "dpx").f; + + if (pj_param (P->ctx, P->params, "tpy").i) + Q->refp.y = pj_param (P->ctx, P->params, "dpy").f; + + if (pj_param (P->ctx, P->params, "tpz").i) + Q->refp.z = pj_param (P->ctx, P->params, "dpz").f; + + + /* Let's help with debugging */ + if (proj_log_level(P->ctx, PJ_LOG_TELL) >= PJ_LOG_DEBUG) { + proj_log_debug(P, "Molodensky-Badekas parameters:"); + proj_log_debug(P, "x= %8.5f y= %8.5f z= %8.5f", Q->xyz_0.x, Q->xyz_0.y, Q->xyz_0.z); + proj_log_debug(P, "rx= %8.5f ry= %8.5f rz= %8.5f", + Q->opk.o / ARCSEC_TO_RAD, Q->opk.p / ARCSEC_TO_RAD, Q->opk.k / ARCSEC_TO_RAD); + proj_log_debug(P, "s= %8.5f exact=%d%s", Q->scale, Q->exact, + Q->is_position_vector ? " convention=position_vector" : + " convention=coordinate_frame"); + proj_log_debug(P, "px= %8.5f py= %8.5f pz= %8.5f", Q->refp.x, Q->refp.y, Q->refp.z); + } + + /* as an optimization, we incorporate the refp in the translation terms */ + Q->xyz_0.x += Q->refp.x; + Q->xyz_0.y += Q->refp.y; + Q->xyz_0.z += Q->refp.z; + + Q->xyz = Q->xyz_0; + + build_rot_matrix(P); + + return P; +} diff --git a/src/PJ_hgridshift.c b/src/PJ_hgridshift.c deleted file mode 100644 index 2919a85c..00000000 --- a/src/PJ_hgridshift.c +++ /dev/null @@ -1,131 +0,0 @@ -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(hgridshift, "Horizontal grid shift"); - -struct pj_opaque_hgridshift { - double t_final; - double t_epoch; -}; - -static XYZ forward_3d(LPZ lpz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.lpz = lpz; - - if (P->gridlist != NULL) { - /* Only try the gridshift if at least one grid is loaded, - * otherwise just pass the coordinate through unchanged. */ - point.lp = proj_hgrid_apply(P, point.lp, PJ_FWD); - } - - return point.xyz; -} - - -static LPZ reverse_3d(XYZ xyz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - point.xyz = xyz; - - if (P->gridlist != NULL) { - /* Only try the gridshift if at least one grid is loaded, - * otherwise just pass the coordinate through unchanged. */ - point.lp = proj_hgrid_apply(P, point.lp, PJ_INV); - } - - return point.lpz; -} - -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; - PJ_COORD point = obs; - - /* If transformation is not time restricted, we always call it */ - if (Q->t_final==0 || Q->t_epoch==0) { - point.xyz = forward_3d (obs.lpz, P); - return point; - } - - /* Time restricted - only apply transform if within time bracket */ - if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) - point.xyz = forward_3d (obs.lpz, P); - - - return point; -} - -static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; - PJ_COORD point = obs; - - /* If transformation is not time restricted, we always call it */ - if (Q->t_final==0 || Q->t_epoch==0) { - point.lpz = reverse_3d (obs.xyz, P); - return point; - } - - /* Time restricted - only apply transform if within time bracket */ - if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) - point.lpz = reverse_3d (obs.xyz, P); - - return point; -} - - -PJ *TRANSFORMATION(hgridshift,0) { - struct pj_opaque_hgridshift *Q = pj_calloc (1, sizeof (struct pj_opaque_hgridshift)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = (void *) Q; - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = 0; - P->inv = 0; - - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - - if (0==pj_param(P->ctx, P->params, "tgrids").i) { - proj_log_error(P, "hgridshift: +grids parameter missing."); - return pj_default_destructor (P, PJD_ERR_NO_ARGS); - } - - /* TODO: Refactor into shared function that can be used */ - /* by both vgridshift and hgridshift */ - if (pj_param(P->ctx, P->params, "tt_final").i) { - Q->t_final = pj_param (P->ctx, P->params, "dt_final").f; - if (Q->t_final == 0) { - /* a number wasn't passed to +t_final, let's see if it was "now" */ - /* and set the time accordingly. */ - if (!strcmp("now", pj_param(P->ctx, P->params, "st_final").s)) { - time_t now; - struct tm *date; - time(&now); - date = localtime(&now); - Q->t_final = 1900.0 + date->tm_year + date->tm_yday/365.0; - } - } - } - - if (pj_param(P->ctx, P->params, "tt_epoch").i) - Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; - - - proj_hgrid_init(P, "grids"); - /* Was gridlist compiled properly? */ - if ( proj_errno(P) ) { - proj_log_error(P, "hgridshift: could not find required grid(s)."); - return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - return P; -} diff --git a/src/PJ_hgridshift.cpp b/src/PJ_hgridshift.cpp new file mode 100644 index 00000000..86c6cdee --- /dev/null +++ b/src/PJ_hgridshift.cpp @@ -0,0 +1,131 @@ +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(hgridshift, "Horizontal grid shift"); + +struct pj_opaque_hgridshift { + double t_final; + double t_epoch; +}; + +static XYZ forward_3d(LPZ lpz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.lpz = lpz; + + if (P->gridlist != NULL) { + /* Only try the gridshift if at least one grid is loaded, + * otherwise just pass the coordinate through unchanged. */ + point.lp = proj_hgrid_apply(P, point.lp, PJ_FWD); + } + + return point.xyz; +} + + +static LPZ reverse_3d(XYZ xyz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + point.xyz = xyz; + + if (P->gridlist != NULL) { + /* Only try the gridshift if at least one grid is loaded, + * otherwise just pass the coordinate through unchanged. */ + point.lp = proj_hgrid_apply(P, point.lp, PJ_INV); + } + + return point.lpz; +} + +static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { + struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; + PJ_COORD point = obs; + + /* If transformation is not time restricted, we always call it */ + if (Q->t_final==0 || Q->t_epoch==0) { + point.xyz = forward_3d (obs.lpz, P); + return point; + } + + /* Time restricted - only apply transform if within time bracket */ + if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) + point.xyz = forward_3d (obs.lpz, P); + + + return point; +} + +static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { + struct pj_opaque_hgridshift *Q = (struct pj_opaque_hgridshift *) P->opaque; + PJ_COORD point = obs; + + /* If transformation is not time restricted, we always call it */ + if (Q->t_final==0 || Q->t_epoch==0) { + point.lpz = reverse_3d (obs.xyz, P); + return point; + } + + /* Time restricted - only apply transform if within time bracket */ + if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) + point.lpz = reverse_3d (obs.xyz, P); + + return point; +} + + +PJ *TRANSFORMATION(hgridshift,0) { + struct pj_opaque_hgridshift *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque_hgridshift))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = (void *) Q; + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = 0; + P->inv = 0; + + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + + if (0==pj_param(P->ctx, P->params, "tgrids").i) { + proj_log_error(P, "hgridshift: +grids parameter missing."); + return pj_default_destructor (P, PJD_ERR_NO_ARGS); + } + + /* TODO: Refactor into shared function that can be used */ + /* by both vgridshift and hgridshift */ + if (pj_param(P->ctx, P->params, "tt_final").i) { + Q->t_final = pj_param (P->ctx, P->params, "dt_final").f; + if (Q->t_final == 0) { + /* a number wasn't passed to +t_final, let's see if it was "now" */ + /* and set the time accordingly. */ + if (!strcmp("now", pj_param(P->ctx, P->params, "st_final").s)) { + time_t now; + struct tm *date; + time(&now); + date = localtime(&now); + Q->t_final = 1900.0 + date->tm_year + date->tm_yday/365.0; + } + } + } + + if (pj_param(P->ctx, P->params, "tt_epoch").i) + Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; + + + proj_hgrid_init(P, "grids"); + /* Was gridlist compiled properly? */ + if ( proj_errno(P) ) { + proj_log_error(P, "hgridshift: could not find required grid(s)."); + return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + return P; +} diff --git a/src/PJ_horner.c b/src/PJ_horner.c deleted file mode 100644 index a6a26e52..00000000 --- a/src/PJ_horner.c +++ /dev/null @@ -1,513 +0,0 @@ -/*********************************************************************** - - Interfacing to a classic piece of geodetic software - -************************************************************************ - - gen_pol is a highly efficient, classic implementation of a generic - 2D Horner's Scheme polynomial evaluation routine by Knud Poder and - Karsten Engsager, originating in the vivid geodetic environment at - what was then (1960-ish) the Danish Geodetic Institute. - - The original Poder/Engsager gen_pol implementation (where - the polynomial degree and two sets of polynomial coefficients - are packed together in one compound array, handled via a plain - double pointer) is compelling and "true to the code history": - - It has a beautiful classical 1960s ring to it, not unlike the - original fft implementations, which revolutionized spectral - analysis in twenty lines of code. - - The Poder coding sound, as classic 1960s as Phil Spector's Wall - of Sound, is beautiful and inimitable. - - On the other hand: For the uninitiated, the gen_pol code is hard - to follow, despite being compact. - - Also, since adding metadata and improving maintainability - of the code are among the implied goals of a current SDFE/DTU Space - project, the material in this file introduces a version with a - more modern (or at least 1990s) look, introducing a "double 2D - polynomial" data type, HORNER. - - Despite introducing a new data type for handling the polynomial - coefficients, great care has been taken to keep the coefficient - array organization identical to that of gen_pol. - - Hence, on one hand, the HORNER data type helps improving the - long term maintainability of the code by making the data - organization more mentally accessible. - - On the other hand, it allows us to preserve the business end of - the original gen_pol implementation - although not including the - famous "Poder dual autocheck" in all its enigmatic elegance. - - ********************************************************************** - - The material included here was written by Knud Poder, starting - around 1960, and Karsten Engsager, starting around 1970. It was - originally written in Algol 60, later (1980s) reimplemented in C. - - The HORNER data type interface, and the organization as a header - library was implemented by Thomas Knudsen, starting around 2015. - - *********************************************************************** - * - * Copyright (c) 2016, SDFE http://www.sdfe.dk / Thomas Knudsen / Karsten Engsager - * - * 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. - * - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(horner, "Horner polynomial evaluation"); - -/* make horner.h interface with proj's memory management */ -#define horner_dealloc(x) pj_dealloc(x) -#define horner_calloc(n,x) pj_calloc(n,x) - - -struct horner; -typedef struct horner HORNER; -static UV horner (const HORNER *transformation, PJ_DIRECTION direction, UV position); -static HORNER *horner_alloc (size_t order, int complex_polynomia); -static void horner_free (HORNER *h); - -struct horner { - int uneg; /* u axis negated? */ - int vneg; /* v axis negated? */ - int order; /* maximum degree of polynomium */ - int coefs; /* number of coefficients for each polynomium */ - double range; /* radius of the region of validity */ - - double *fwd_u; /* coefficients for the forward transformations */ - double *fwd_v; /* i.e. latitude/longitude to northing/easting */ - - double *inv_u; /* coefficients for the inverse transformations */ - double *inv_v; /* i.e. northing/easting to latitude/longitude */ - - double *fwd_c; /* coefficients for the complex forward transformations */ - double *inv_c; /* coefficients for the complex inverse transformations */ - - UV *fwd_origin; /* False longitude/latitude */ - UV *inv_origin; /* False easting/northing */ -}; - -/* e.g. degree = 2: a + bx + cy + dxx + eyy + fxy, i.e. 6 coefficients */ -#define horner_number_of_coefficients(order) \ - (((order + 1)*(order + 2)/2)) - - -static void horner_free (HORNER *h) { - horner_dealloc (h->inv_v); - horner_dealloc (h->inv_u); - horner_dealloc (h->fwd_v); - horner_dealloc (h->fwd_u); - horner_dealloc (h->fwd_c); - horner_dealloc (h->inv_c); - horner_dealloc (h->fwd_origin); - horner_dealloc (h->inv_origin); - horner_dealloc (h); -} - - -static HORNER *horner_alloc (size_t order, int complex_polynomia) { - /* size_t is unsigned, so we need not check for order > 0 */ - int n = (int)horner_number_of_coefficients(order); - int polynomia_ok = 0; - HORNER *h = horner_calloc (1, sizeof (HORNER)); - - if (0==h) - return 0; - - if (complex_polynomia) - n = 2*(int)order + 2; - h->order = (int)order; - h->coefs = n; - - if (complex_polynomia) { - h->fwd_c = horner_calloc (n, sizeof(double)); - h->inv_c = horner_calloc (n, sizeof(double)); - if (h->fwd_c && h->inv_c) - polynomia_ok = 1; - } - else { - h->fwd_u = horner_calloc (n, sizeof(double)); - h->fwd_v = horner_calloc (n, sizeof(double)); - h->inv_u = horner_calloc (n, sizeof(double)); - h->inv_v = horner_calloc (n, sizeof(double)); - if (h->fwd_u && h->fwd_v && h->inv_u && h->inv_v) - polynomia_ok = 1; - } - - h->fwd_origin = horner_calloc (1, sizeof(UV)); - h->inv_origin = horner_calloc (1, sizeof(UV)); - - if (polynomia_ok && h->fwd_origin && h->inv_origin) - return h; - - /* safe, since all pointers are null-initialized (by calloc) */ - horner_free (h); - return 0; -} - - - - -/**********************************************************************/ -static UV horner (const HORNER *transformation, PJ_DIRECTION direction, UV position) { -/*********************************************************************** - -A reimplementation of the classic Engsager/Poder 2D Horner polynomial -evaluation engine "gen_pol". - -This version omits the inimitable Poder "dual autocheck"-machinery, -which here is intended to be implemented at a higher level of the -library: We separate the polynomial evaluation from the quality -control (which, given the limited MTBF for "computing machinery", -typical when Knud Poder invented the dual autocheck method, -was not defensible at that time). - -Another difference from the original version is that we return the -result on the stack, rather than accepting pointers to result variables -as input. This results in code that is easy to read: - - projected = horner (s34j, 1, geographic); - geographic = horner (s34j, -1, projected ); - -and experiments have shown that on contemporary architectures, the time -taken for returning even comparatively large objects on the stack (and -the UV is not that large - typically only 16 bytes) is negligibly -different from passing two pointers (i.e. typically also 16 bytes) the -other way. - -The polynomium has the form: - -P = sum (i = [0 : order]) - sum (j = [0 : order - i]) - pow(par_1, i) * pow(par_2, j) * coef(index(order, i, j)) - -For numerical stability, the summation is carried out backwards, -summing the tiny high order elements first. - -***********************************************************************/ - - /* These variable names follow the Engsager/Poder implementation */ - int sz; /* Number of coefficients per polynomial */ - double *tcx, *tcy; /* Coefficient pointers */ - double range; /* Equivalent to the gen_pol's FLOATLIMIT constant */ - double n, e; - UV uv_error; - uv_error.u = uv_error.v = HUGE_VAL; - - if (0==transformation) - return uv_error; - - /* Check for valid value of direction (-1, 0, 1) */ - switch (direction) { - case PJ_IDENT: /* no-op */ - return position; - case PJ_FWD: /* forward */ - case PJ_INV: /* inverse */ - break; - default: /* invalid */ - errno = EINVAL; - return uv_error; - } - - /* Prepare for double Horner */ - sz = horner_number_of_coefficients(transformation->order); - range = transformation->range; - - - if (direction==PJ_FWD) { /* forward */ - tcx = transformation->fwd_u + sz; - tcy = transformation->fwd_v + sz; - e = position.u - transformation->fwd_origin->u; - n = position.v - transformation->fwd_origin->v; - } else { /* inverse */ - tcx = transformation->inv_u + sz; - tcy = transformation->inv_v + sz; - e = position.u - transformation->inv_origin->u; - n = position.v - transformation->inv_origin->v; - } - - if ((fabs(n) > range) || (fabs(e) > range)) { - errno = EDOM; - return uv_error; - } - - /* The melody of this block is straight out of the great Engsager/Poder songbook */ - else { - int g = transformation->order; - int r = g, c; - double u, v, N, E; - - /* Double Horner's scheme: N = n*Cy*e -> yout, E = e*Cx*n -> xout */ - N = *--tcy; - E = *--tcx; - for (; r > 0; r--) { - u = *--tcy; - v = *--tcx; - for (c = g; c >= r; c--) { - u = n*u + *--tcy; - v = e*v + *--tcx; - } - N = e*N + u; - E = n*E + v; - } - - position.u = E; - position.v = N; - } - - return position; -} - - - - - - - -static PJ_COORD horner_forward_4d (PJ_COORD point, PJ *P) { - point.uv = horner ((HORNER *) P->opaque, 1, point.uv); - return point; -} - -static PJ_COORD horner_reverse_4d (PJ_COORD point, PJ *P) { - point.uv = horner ((HORNER *) P->opaque, -1, point.uv); - return point; -} - - - - -/**********************************************************************/ -static UV complex_horner (const HORNER *transformation, PJ_DIRECTION direction, UV position) { -/*********************************************************************** - -A reimplementation of a classic Engsager/Poder Horner complex -polynomial evaluation engine. - -***********************************************************************/ - - /* These variable names follow the Engsager/Poder implementation */ - int sz; /* Number of coefficients */ - double *c, *cb; /* Coefficient pointers */ - double range; /* Equivalent to the gen_pol's FLOATLIMIT constant */ - double n, e, w, N, E; - UV uv_error; - uv_error.u = uv_error.v = HUGE_VAL; - - if (0==transformation) - return uv_error; - - /* Check for valid value of direction (-1, 0, 1) */ - switch (direction) { - case PJ_IDENT: /* no-op */ - return position; - case PJ_FWD: /* forward */ - case PJ_INV: /* inverse */ - break; - default: /* invalid */ - errno = EINVAL; - return uv_error; - } - - /* Prepare for double Horner */ - sz = 2*transformation->order + 2; - range = transformation->range; - - if (direction==PJ_FWD) { /* forward */ - cb = transformation->fwd_c; - c = cb + sz; - e = position.u - transformation->fwd_origin->u; - n = position.v - transformation->fwd_origin->v; - if (transformation->uneg) - e = -e; - if (transformation->vneg) - n = -n; - } else { /* inverse */ - cb = transformation->inv_c; - c = cb + sz; - e = position.u - transformation->inv_origin->u; - n = position.v - transformation->inv_origin->v; - if (transformation->uneg) - e = -e; - if (transformation->vneg) - n = -n; - } - - if ((fabs(n) > range) || (fabs(e) > range)) { - errno = EDOM; - return uv_error; - } - - /* Everything's set up properly - now do the actual polynomium evaluation */ - E = *--c; - N = *--c; - while (c > cb) { - w = n*E + e*N + *--c; - N = n*N - e*E + *--c; - E = w; - } - - position.u = E; - position.v = N; - return position; -} - - - -static PJ_COORD complex_horner_forward_4d (PJ_COORD point, PJ *P) { - point.uv = complex_horner ((HORNER *) P->opaque, PJ_FWD, point.uv); - return point; -} - -static PJ_COORD complex_horner_reverse_4d (PJ_COORD point, PJ *P) { - point.uv = complex_horner ((HORNER *) P->opaque, PJ_INV, point.uv); - return point; -} - - -static void *horner_freeup (PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - if (0==P->opaque) - return pj_default_destructor (P, errlev); - horner_free ((HORNER *) P->opaque); - P->opaque = 0; - return pj_default_destructor (P, errlev); -} - - -static int parse_coefs (PJ *P, double *coefs, char *param, int ncoefs) { - char *buf, *init, *next = 0; - int i; - - buf = pj_calloc (strlen (param) + 2, sizeof(char)); - if (0==buf) { - proj_log_error (P, "Horner: No memory left"); - return 0; - } - - sprintf (buf, "t%s", param); - if (0==pj_param (P->ctx, P->params, buf).i) { - pj_dealloc (buf); - return 0; - } - sprintf (buf, "s%s", param); - init = pj_param(P->ctx, P->params, buf).s; - pj_dealloc (buf); - - for (i = 0; i < ncoefs; i++) { - if (i > 0) { - if ( next == 0 || ','!=*next) { - proj_log_error (P, "Horner: Malformed polynomium set %s. need %d coefs", param, ncoefs); - return 0; - } - init = ++next; - } - coefs[i] = pj_strtod (init, &next); - } - return 1; -} - - -/*********************************************************************/ -PJ *PROJECTION(horner) { -/*********************************************************************/ - int degree = 0, n, complex_polynomia = 0; - HORNER *Q; - P->fwd4d = horner_forward_4d; - P->inv4d = horner_reverse_4d; - P->fwd3d = 0; - P->inv3d = 0; - P->fwd = 0; - P->inv = 0; - P->left = P->right = PJ_IO_UNITS_PROJECTED; - P->destructor = horner_freeup; - - /* Polynomial degree specified? */ - if (pj_param (P->ctx, P->params, "tdeg").i) { /* degree specified? */ - degree = pj_param(P->ctx, P->params, "ideg").i; - if (degree < 0 || degree > 10000) { - /* What are reasonable minimum and maximums for degree? */ - proj_log_debug (P, "Horner: Degree is unreasonable: %d", degree); - return horner_freeup (P, PJD_ERR_INVALID_ARG); - } - } else { - proj_log_debug (P, "Horner: Must specify polynomial degree, (+deg=n)"); - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - } - - if (pj_param (P->ctx, P->params, "tfwd_c").i || pj_param (P->ctx, P->params, "tinv_c").i) /* complex polynomium? */ - complex_polynomia = 1; - - Q = horner_alloc (degree, complex_polynomia); - if (Q == 0) - return horner_freeup (P, ENOMEM); - P->opaque = (void *) Q; - - if (complex_polynomia) { - /* Westings and/or southings? */ - Q->uneg = pj_param_exists (P->params, "uneg") ? 1 : 0; - Q->vneg = pj_param_exists (P->params, "vneg") ? 1 : 0; - - n = 2*degree + 2; - if (0==parse_coefs (P, Q->fwd_c, "fwd_c", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, Q->inv_c, "inv_c", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - P->fwd4d = complex_horner_forward_4d; - P->inv4d = complex_horner_reverse_4d; - } - - else { - n = horner_number_of_coefficients (degree); - if (0==parse_coefs (P, Q->fwd_u, "fwd_u", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, Q->fwd_v, "fwd_v", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, Q->inv_u, "inv_u", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, Q->inv_v, "inv_v", n)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - } - - if (0==parse_coefs (P, (double *)(Q->fwd_origin), "fwd_origin", 2)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, (double *)(Q->inv_origin), "inv_origin", 2)) - return horner_freeup (P, PJD_ERR_MISSING_ARGS); - if (0==parse_coefs (P, &Q->range, "range", 1)) - Q->range = 500000; - - return P; -} diff --git a/src/PJ_horner.cpp b/src/PJ_horner.cpp new file mode 100644 index 00000000..49e108c8 --- /dev/null +++ b/src/PJ_horner.cpp @@ -0,0 +1,513 @@ +/*********************************************************************** + + Interfacing to a classic piece of geodetic software + +************************************************************************ + + gen_pol is a highly efficient, classic implementation of a generic + 2D Horner's Scheme polynomial evaluation routine by Knud Poder and + Karsten Engsager, originating in the vivid geodetic environment at + what was then (1960-ish) the Danish Geodetic Institute. + + The original Poder/Engsager gen_pol implementation (where + the polynomial degree and two sets of polynomial coefficients + are packed together in one compound array, handled via a plain + double pointer) is compelling and "true to the code history": + + It has a beautiful classical 1960s ring to it, not unlike the + original fft implementations, which revolutionized spectral + analysis in twenty lines of code. + + The Poder coding sound, as classic 1960s as Phil Spector's Wall + of Sound, is beautiful and inimitable. + + On the other hand: For the uninitiated, the gen_pol code is hard + to follow, despite being compact. + + Also, since adding metadata and improving maintainability + of the code are among the implied goals of a current SDFE/DTU Space + project, the material in this file introduces a version with a + more modern (or at least 1990s) look, introducing a "double 2D + polynomial" data type, HORNER. + + Despite introducing a new data type for handling the polynomial + coefficients, great care has been taken to keep the coefficient + array organization identical to that of gen_pol. + + Hence, on one hand, the HORNER data type helps improving the + long term maintainability of the code by making the data + organization more mentally accessible. + + On the other hand, it allows us to preserve the business end of + the original gen_pol implementation - although not including the + famous "Poder dual autocheck" in all its enigmatic elegance. + + ********************************************************************** + + The material included here was written by Knud Poder, starting + around 1960, and Karsten Engsager, starting around 1970. It was + originally written in Algol 60, later (1980s) reimplemented in C. + + The HORNER data type interface, and the organization as a header + library was implemented by Thomas Knudsen, starting around 2015. + + *********************************************************************** + * + * Copyright (c) 2016, SDFE http://www.sdfe.dk / Thomas Knudsen / Karsten Engsager + * + * 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. + * + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(horner, "Horner polynomial evaluation"); + +/* make horner.h interface with proj's memory management */ +#define horner_dealloc(x) pj_dealloc(x) +#define horner_calloc(n,x) pj_calloc(n,x) + + +struct horner; +typedef struct horner HORNER; +static UV horner (const HORNER *transformation, PJ_DIRECTION direction, UV position); +static HORNER *horner_alloc (size_t order, int complex_polynomia); +static void horner_free (HORNER *h); + +struct horner { + int uneg; /* u axis negated? */ + int vneg; /* v axis negated? */ + int order; /* maximum degree of polynomium */ + int coefs; /* number of coefficients for each polynomium */ + double range; /* radius of the region of validity */ + + double *fwd_u; /* coefficients for the forward transformations */ + double *fwd_v; /* i.e. latitude/longitude to northing/easting */ + + double *inv_u; /* coefficients for the inverse transformations */ + double *inv_v; /* i.e. northing/easting to latitude/longitude */ + + double *fwd_c; /* coefficients for the complex forward transformations */ + double *inv_c; /* coefficients for the complex inverse transformations */ + + UV *fwd_origin; /* False longitude/latitude */ + UV *inv_origin; /* False easting/northing */ +}; + +/* e.g. degree = 2: a + bx + cy + dxx + eyy + fxy, i.e. 6 coefficients */ +#define horner_number_of_coefficients(order) \ + (((order + 1)*(order + 2)/2)) + + +static void horner_free (HORNER *h) { + horner_dealloc (h->inv_v); + horner_dealloc (h->inv_u); + horner_dealloc (h->fwd_v); + horner_dealloc (h->fwd_u); + horner_dealloc (h->fwd_c); + horner_dealloc (h->inv_c); + horner_dealloc (h->fwd_origin); + horner_dealloc (h->inv_origin); + horner_dealloc (h); +} + + +static HORNER *horner_alloc (size_t order, int complex_polynomia) { + /* size_t is unsigned, so we need not check for order > 0 */ + int n = (int)horner_number_of_coefficients(order); + int polynomia_ok = 0; + HORNER *h = static_cast(horner_calloc (1, sizeof (HORNER))); + + if (0==h) + return 0; + + if (complex_polynomia) + n = 2*(int)order + 2; + h->order = (int)order; + h->coefs = n; + + if (complex_polynomia) { + h->fwd_c = static_cast(horner_calloc (n, sizeof(double))); + h->inv_c = static_cast(horner_calloc (n, sizeof(double))); + if (h->fwd_c && h->inv_c) + polynomia_ok = 1; + } + else { + h->fwd_u = static_cast(horner_calloc (n, sizeof(double))); + h->fwd_v = static_cast(horner_calloc (n, sizeof(double))); + h->inv_u = static_cast(horner_calloc (n, sizeof(double))); + h->inv_v = static_cast(horner_calloc (n, sizeof(double))); + if (h->fwd_u && h->fwd_v && h->inv_u && h->inv_v) + polynomia_ok = 1; + } + + h->fwd_origin = static_cast(horner_calloc (1, sizeof(UV))); + h->inv_origin = static_cast(horner_calloc (1, sizeof(UV))); + + if (polynomia_ok && h->fwd_origin && h->inv_origin) + return h; + + /* safe, since all pointers are null-initialized (by calloc) */ + horner_free (h); + return 0; +} + + + + +/**********************************************************************/ +static UV horner (const HORNER *transformation, PJ_DIRECTION direction, UV position) { +/*********************************************************************** + +A reimplementation of the classic Engsager/Poder 2D Horner polynomial +evaluation engine "gen_pol". + +This version omits the inimitable Poder "dual autocheck"-machinery, +which here is intended to be implemented at a higher level of the +library: We separate the polynomial evaluation from the quality +control (which, given the limited MTBF for "computing machinery", +typical when Knud Poder invented the dual autocheck method, +was not defensible at that time). + +Another difference from the original version is that we return the +result on the stack, rather than accepting pointers to result variables +as input. This results in code that is easy to read: + + projected = horner (s34j, 1, geographic); + geographic = horner (s34j, -1, projected ); + +and experiments have shown that on contemporary architectures, the time +taken for returning even comparatively large objects on the stack (and +the UV is not that large - typically only 16 bytes) is negligibly +different from passing two pointers (i.e. typically also 16 bytes) the +other way. + +The polynomium has the form: + +P = sum (i = [0 : order]) + sum (j = [0 : order - i]) + pow(par_1, i) * pow(par_2, j) * coef(index(order, i, j)) + +For numerical stability, the summation is carried out backwards, +summing the tiny high order elements first. + +***********************************************************************/ + + /* These variable names follow the Engsager/Poder implementation */ + int sz; /* Number of coefficients per polynomial */ + double *tcx, *tcy; /* Coefficient pointers */ + double range; /* Equivalent to the gen_pol's FLOATLIMIT constant */ + double n, e; + UV uv_error; + uv_error.u = uv_error.v = HUGE_VAL; + + if (0==transformation) + return uv_error; + + /* Check for valid value of direction (-1, 0, 1) */ + switch (direction) { + case PJ_IDENT: /* no-op */ + return position; + case PJ_FWD: /* forward */ + case PJ_INV: /* inverse */ + break; + default: /* invalid */ + errno = EINVAL; + return uv_error; + } + + /* Prepare for double Horner */ + sz = horner_number_of_coefficients(transformation->order); + range = transformation->range; + + + if (direction==PJ_FWD) { /* forward */ + tcx = transformation->fwd_u + sz; + tcy = transformation->fwd_v + sz; + e = position.u - transformation->fwd_origin->u; + n = position.v - transformation->fwd_origin->v; + } else { /* inverse */ + tcx = transformation->inv_u + sz; + tcy = transformation->inv_v + sz; + e = position.u - transformation->inv_origin->u; + n = position.v - transformation->inv_origin->v; + } + + if ((fabs(n) > range) || (fabs(e) > range)) { + errno = EDOM; + return uv_error; + } + + /* The melody of this block is straight out of the great Engsager/Poder songbook */ + else { + int g = transformation->order; + int r = g, c; + double u, v, N, E; + + /* Double Horner's scheme: N = n*Cy*e -> yout, E = e*Cx*n -> xout */ + N = *--tcy; + E = *--tcx; + for (; r > 0; r--) { + u = *--tcy; + v = *--tcx; + for (c = g; c >= r; c--) { + u = n*u + *--tcy; + v = e*v + *--tcx; + } + N = e*N + u; + E = n*E + v; + } + + position.u = E; + position.v = N; + } + + return position; +} + + + + + + + +static PJ_COORD horner_forward_4d (PJ_COORD point, PJ *P) { + point.uv = horner ((HORNER *) P->opaque, PJ_FWD, point.uv); + return point; +} + +static PJ_COORD horner_reverse_4d (PJ_COORD point, PJ *P) { + point.uv = horner ((HORNER *) P->opaque, PJ_INV, point.uv); + return point; +} + + + + +/**********************************************************************/ +static UV complex_horner (const HORNER *transformation, PJ_DIRECTION direction, UV position) { +/*********************************************************************** + +A reimplementation of a classic Engsager/Poder Horner complex +polynomial evaluation engine. + +***********************************************************************/ + + /* These variable names follow the Engsager/Poder implementation */ + int sz; /* Number of coefficients */ + double *c, *cb; /* Coefficient pointers */ + double range; /* Equivalent to the gen_pol's FLOATLIMIT constant */ + double n, e, w, N, E; + UV uv_error; + uv_error.u = uv_error.v = HUGE_VAL; + + if (0==transformation) + return uv_error; + + /* Check for valid value of direction (-1, 0, 1) */ + switch (direction) { + case PJ_IDENT: /* no-op */ + return position; + case PJ_FWD: /* forward */ + case PJ_INV: /* inverse */ + break; + default: /* invalid */ + errno = EINVAL; + return uv_error; + } + + /* Prepare for double Horner */ + sz = 2*transformation->order + 2; + range = transformation->range; + + if (direction==PJ_FWD) { /* forward */ + cb = transformation->fwd_c; + c = cb + sz; + e = position.u - transformation->fwd_origin->u; + n = position.v - transformation->fwd_origin->v; + if (transformation->uneg) + e = -e; + if (transformation->vneg) + n = -n; + } else { /* inverse */ + cb = transformation->inv_c; + c = cb + sz; + e = position.u - transformation->inv_origin->u; + n = position.v - transformation->inv_origin->v; + if (transformation->uneg) + e = -e; + if (transformation->vneg) + n = -n; + } + + if ((fabs(n) > range) || (fabs(e) > range)) { + errno = EDOM; + return uv_error; + } + + /* Everything's set up properly - now do the actual polynomium evaluation */ + E = *--c; + N = *--c; + while (c > cb) { + w = n*E + e*N + *--c; + N = n*N - e*E + *--c; + E = w; + } + + position.u = E; + position.v = N; + return position; +} + + + +static PJ_COORD complex_horner_forward_4d (PJ_COORD point, PJ *P) { + point.uv = complex_horner ((HORNER *) P->opaque, PJ_FWD, point.uv); + return point; +} + +static PJ_COORD complex_horner_reverse_4d (PJ_COORD point, PJ *P) { + point.uv = complex_horner ((HORNER *) P->opaque, PJ_INV, point.uv); + return point; +} + + +static PJ *horner_freeup (PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_default_destructor (P, errlev); + horner_free ((HORNER *) P->opaque); + P->opaque = 0; + return pj_default_destructor (P, errlev); +} + + +static int parse_coefs (PJ *P, double *coefs, char *param, int ncoefs) { + char *buf, *init, *next = 0; + int i; + + buf = static_cast(pj_calloc (strlen (param) + 2, sizeof(char))); + if (0==buf) { + proj_log_error (P, "Horner: No memory left"); + return 0; + } + + sprintf (buf, "t%s", param); + if (0==pj_param (P->ctx, P->params, buf).i) { + pj_dealloc (buf); + return 0; + } + sprintf (buf, "s%s", param); + init = pj_param(P->ctx, P->params, buf).s; + pj_dealloc (buf); + + for (i = 0; i < ncoefs; i++) { + if (i > 0) { + if ( next == 0 || ','!=*next) { + proj_log_error (P, "Horner: Malformed polynomium set %s. need %d coefs", param, ncoefs); + return 0; + } + init = ++next; + } + coefs[i] = pj_strtod (init, &next); + } + return 1; +} + + +/*********************************************************************/ +PJ *PROJECTION(horner) { +/*********************************************************************/ + int degree = 0, n, complex_polynomia = 0; + HORNER *Q; + P->fwd4d = horner_forward_4d; + P->inv4d = horner_reverse_4d; + P->fwd3d = 0; + P->inv3d = 0; + P->fwd = 0; + P->inv = 0; + P->left = P->right = PJ_IO_UNITS_PROJECTED; + P->destructor = horner_freeup; + + /* Polynomial degree specified? */ + if (pj_param (P->ctx, P->params, "tdeg").i) { /* degree specified? */ + degree = pj_param(P->ctx, P->params, "ideg").i; + if (degree < 0 || degree > 10000) { + /* What are reasonable minimum and maximums for degree? */ + proj_log_debug (P, "Horner: Degree is unreasonable: %d", degree); + return horner_freeup (P, PJD_ERR_INVALID_ARG); + } + } else { + proj_log_debug (P, "Horner: Must specify polynomial degree, (+deg=n)"); + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + } + + if (pj_param (P->ctx, P->params, "tfwd_c").i || pj_param (P->ctx, P->params, "tinv_c").i) /* complex polynomium? */ + complex_polynomia = 1; + + Q = horner_alloc (degree, complex_polynomia); + if (Q == 0) + return horner_freeup (P, ENOMEM); + P->opaque = Q; + + if (complex_polynomia) { + /* Westings and/or southings? */ + Q->uneg = pj_param_exists (P->params, "uneg") ? 1 : 0; + Q->vneg = pj_param_exists (P->params, "vneg") ? 1 : 0; + + n = 2*degree + 2; + if (0==parse_coefs (P, Q->fwd_c, "fwd_c", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, Q->inv_c, "inv_c", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + P->fwd4d = complex_horner_forward_4d; + P->inv4d = complex_horner_reverse_4d; + } + + else { + n = horner_number_of_coefficients (degree); + if (0==parse_coefs (P, Q->fwd_u, "fwd_u", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, Q->fwd_v, "fwd_v", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, Q->inv_u, "inv_u", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, Q->inv_v, "inv_v", n)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + } + + if (0==parse_coefs (P, (double *)(Q->fwd_origin), "fwd_origin", 2)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, (double *)(Q->inv_origin), "inv_origin", 2)) + return horner_freeup (P, PJD_ERR_MISSING_ARGS); + if (0==parse_coefs (P, &Q->range, "range", 1)) + Q->range = 500000; + + return P; +} diff --git a/src/PJ_igh.c b/src/PJ_igh.c deleted file mode 100644 index c991649a..00000000 --- a/src/PJ_igh.c +++ /dev/null @@ -1,225 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(igh, "Interrupted Goode Homolosine") "\n\tPCyl, Sph"; - -C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *); - -/* 40d 44' 11.8" [degrees] */ -/* -static const double d4044118 = (40 + 44/60. + 11.8/3600.) * DEG_TO_RAD; -has been replaced by this define, to eliminate portability issue: -Initializer element not computable at load time -*/ -#define d4044118 ((40 + 44/60. + 11.8/3600.) * DEG_TO_RAD) - -static const double d10 = 10 * DEG_TO_RAD; -static const double d20 = 20 * DEG_TO_RAD; -static const double d30 = 30 * DEG_TO_RAD; -static const double d40 = 40 * DEG_TO_RAD; -static const double d50 = 50 * DEG_TO_RAD; -static const double d60 = 60 * DEG_TO_RAD; -static const double d80 = 80 * DEG_TO_RAD; -static const double d90 = 90 * DEG_TO_RAD; -static const double d100 = 100 * DEG_TO_RAD; -static const double d140 = 140 * DEG_TO_RAD; -static const double d160 = 160 * DEG_TO_RAD; -static const double d180 = 180 * DEG_TO_RAD; - -static const double EPSLN = 1.e-10; /* allow a little 'slack' on zone edge positions */ - -struct pj_opaque { - struct PJconsts* pj[12]; \ - double dy0; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy; - struct pj_opaque *Q = P->opaque; - int z; - - if (lp.phi >= d4044118) { /* 1|2 */ - z = (lp.lam <= -d40 ? 1: 2); - } - else if (lp.phi >= 0) { /* 3|4 */ - z = (lp.lam <= -d40 ? 3: 4); - } - else if (lp.phi >= -d4044118) { /* 5|6|7|8 */ - if (lp.lam <= -d100) z = 5; /* 5 */ - else if (lp.lam <= -d20) z = 6; /* 6 */ - else if (lp.lam <= d80) z = 7; /* 7 */ - else z = 8; /* 8 */ - } - else { /* 9|10|11|12 */ - if (lp.lam <= -d100) z = 9; /* 9 */ - else if (lp.lam <= -d20) z = 10; /* 10 */ - else if (lp.lam <= d80) z = 11; /* 11 */ - else z = 12; /* 12 */ - } - - lp.lam -= Q->pj[z-1]->lam0; - xy = Q->pj[z-1]->fwd(lp, Q->pj[z-1]); - xy.x += Q->pj[z-1]->x0; - xy.y += Q->pj[z-1]->y0; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - const double y90 = Q->dy0 + sqrt(2); /* lt=90 corresponds to y=y0+sqrt(2) */ - - int z = 0; - if (xy.y > y90+EPSLN || xy.y < -y90+EPSLN) /* 0 */ - z = 0; - else if (xy.y >= d4044118) /* 1|2 */ - z = (xy.x <= -d40? 1: 2); - else if (xy.y >= 0) /* 3|4 */ - z = (xy.x <= -d40? 3: 4); - else if (xy.y >= -d4044118) { /* 5|6|7|8 */ - if (xy.x <= -d100) z = 5; /* 5 */ - else if (xy.x <= -d20) z = 6; /* 6 */ - else if (xy.x <= d80) z = 7; /* 7 */ - else z = 8; /* 8 */ - } - else { /* 9|10|11|12 */ - if (xy.x <= -d100) z = 9; /* 9 */ - else if (xy.x <= -d20) z = 10; /* 10 */ - else if (xy.x <= d80) z = 11; /* 11 */ - else z = 12; /* 12 */ - } - - if (z) { - int ok = 0; - - xy.x -= Q->pj[z-1]->x0; - xy.y -= Q->pj[z-1]->y0; - lp = Q->pj[z-1]->inv(xy, Q->pj[z-1]); - lp.lam += Q->pj[z-1]->lam0; - - switch (z) { - case 1: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN) || - ((lp.lam >= -d40-EPSLN && lp.lam <= -d10+EPSLN) && - (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; - case 2: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN) || - ((lp.lam >= -d180-EPSLN && lp.lam <= -d160+EPSLN) && - (lp.phi >= d50-EPSLN && lp.phi <= d90+EPSLN)) || - ((lp.lam >= -d50-EPSLN && lp.lam <= -d40+EPSLN) && - (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; - case 3: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN); break; - case 4: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN); break; - case 5: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; - case 6: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; - case 7: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; - case 8: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; - case 9: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; - case 10: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; - case 11: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; - case 12: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; - } - z = (!ok? 0: z); /* projectable? */ - } - - if (!z) lp.lam = HUGE_VAL; - if (!z) lp.phi = HUGE_VAL; - - return lp; -} - - -static void *destructor (PJ *P, int errlev) { - int i; - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - for (i = 0; i < 12; ++i) { - if (P->opaque->pj[i]) - P->opaque->pj[i]->destructor(P->opaque->pj[i], errlev); - } - - return pj_default_destructor(P, errlev); -} - - - -/* - Zones: - - -180 -40 180 - +--------------+-------------------------+ Zones 1,2,9,10,11 & 12: - |1 |2 | Mollweide projection - | | | - +--------------+-------------------------+ Zones 3,4,5,6,7 & 8: - |3 |4 | Sinusoidal projection - | | | - 0 +-------+------+-+-----------+-----------+ - |5 |6 |7 |8 | - | | | | | - +-------+--------+-----------+-----------+ - |9 |10 |11 |12 | - | | | | | - +-------+--------+-----------+-----------+ - -180 -100 -20 80 180 -*/ - -#define SETUP(n, proj, x_0, y_0, lon_0) \ - if (!(Q->pj[n-1] = pj_##proj(0))) return destructor(P, ENOMEM); \ - if (!(Q->pj[n-1] = pj_##proj(Q->pj[n-1]))) return destructor(P, ENOMEM); \ - Q->pj[n-1]->ctx = P->ctx; \ - Q->pj[n-1]->x0 = x_0; \ - Q->pj[n-1]->y0 = y_0; \ - Q->pj[n-1]->lam0 = lon_0; - - -PJ *PROJECTION(igh) { - XY xy1, xy3; - LP lp = { 0, d4044118 }; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - - /* sinusoidal zones */ - SETUP(3, sinu, -d100, 0, -d100); - SETUP(4, sinu, d30, 0, d30); - SETUP(5, sinu, -d160, 0, -d160); - SETUP(6, sinu, -d60, 0, -d60); - SETUP(7, sinu, d20, 0, d20); - SETUP(8, sinu, d140, 0, d140); - - /* mollweide zones */ - SETUP(1, moll, -d100, 0, -d100); - - /* y0 ? */ - xy1 = Q->pj[0]->fwd(lp, Q->pj[0]); /* zone 1 */ - xy3 = Q->pj[2]->fwd(lp, Q->pj[2]); /* zone 3 */ - /* y0 + xy1.y = xy3.y for lt = 40d44'11.8" */ - Q->dy0 = xy3.y - xy1.y; - - Q->pj[0]->y0 = Q->dy0; - - /* mollweide zones (cont'd) */ - SETUP( 2, moll, d30, Q->dy0, d30); - SETUP( 9, moll, -d160, -Q->dy0, -d160); - SETUP(10, moll, -d60, -Q->dy0, -d60); - SETUP(11, moll, d20, -Q->dy0, d20); - SETUP(12, moll, d140, -Q->dy0, d140); - - P->inv = s_inverse; - P->fwd = s_forward; - P->destructor = destructor; - P->es = 0.; - - return P; -} diff --git a/src/PJ_igh.cpp b/src/PJ_igh.cpp new file mode 100644 index 00000000..476d1c6b --- /dev/null +++ b/src/PJ_igh.cpp @@ -0,0 +1,225 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(igh, "Interrupted Goode Homolosine") "\n\tPCyl, Sph"; + +C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *); + +/* 40d 44' 11.8" [degrees] */ +/* +static const double d4044118 = (40 + 44/60. + 11.8/3600.) * DEG_TO_RAD; +has been replaced by this define, to eliminate portability issue: +Initializer element not computable at load time +*/ +#define d4044118 ((40 + 44/60. + 11.8/3600.) * DEG_TO_RAD) + +static const double d10 = 10 * DEG_TO_RAD; +static const double d20 = 20 * DEG_TO_RAD; +static const double d30 = 30 * DEG_TO_RAD; +static const double d40 = 40 * DEG_TO_RAD; +static const double d50 = 50 * DEG_TO_RAD; +static const double d60 = 60 * DEG_TO_RAD; +static const double d80 = 80 * DEG_TO_RAD; +static const double d90 = 90 * DEG_TO_RAD; +static const double d100 = 100 * DEG_TO_RAD; +static const double d140 = 140 * DEG_TO_RAD; +static const double d160 = 160 * DEG_TO_RAD; +static const double d180 = 180 * DEG_TO_RAD; + +static const double EPSLN = 1.e-10; /* allow a little 'slack' on zone edge positions */ + +struct pj_opaque { + struct PJconsts* pj[12]; \ + double dy0; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy; + struct pj_opaque *Q = static_cast(P->opaque); + int z; + + if (lp.phi >= d4044118) { /* 1|2 */ + z = (lp.lam <= -d40 ? 1: 2); + } + else if (lp.phi >= 0) { /* 3|4 */ + z = (lp.lam <= -d40 ? 3: 4); + } + else if (lp.phi >= -d4044118) { /* 5|6|7|8 */ + if (lp.lam <= -d100) z = 5; /* 5 */ + else if (lp.lam <= -d20) z = 6; /* 6 */ + else if (lp.lam <= d80) z = 7; /* 7 */ + else z = 8; /* 8 */ + } + else { /* 9|10|11|12 */ + if (lp.lam <= -d100) z = 9; /* 9 */ + else if (lp.lam <= -d20) z = 10; /* 10 */ + else if (lp.lam <= d80) z = 11; /* 11 */ + else z = 12; /* 12 */ + } + + lp.lam -= Q->pj[z-1]->lam0; + xy = Q->pj[z-1]->fwd(lp, Q->pj[z-1]); + xy.x += Q->pj[z-1]->x0; + xy.y += Q->pj[z-1]->y0; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + const double y90 = Q->dy0 + sqrt(2); /* lt=90 corresponds to y=y0+sqrt(2) */ + + int z = 0; + if (xy.y > y90+EPSLN || xy.y < -y90+EPSLN) /* 0 */ + z = 0; + else if (xy.y >= d4044118) /* 1|2 */ + z = (xy.x <= -d40? 1: 2); + else if (xy.y >= 0) /* 3|4 */ + z = (xy.x <= -d40? 3: 4); + else if (xy.y >= -d4044118) { /* 5|6|7|8 */ + if (xy.x <= -d100) z = 5; /* 5 */ + else if (xy.x <= -d20) z = 6; /* 6 */ + else if (xy.x <= d80) z = 7; /* 7 */ + else z = 8; /* 8 */ + } + else { /* 9|10|11|12 */ + if (xy.x <= -d100) z = 9; /* 9 */ + else if (xy.x <= -d20) z = 10; /* 10 */ + else if (xy.x <= d80) z = 11; /* 11 */ + else z = 12; /* 12 */ + } + + if (z) { + int ok = 0; + + xy.x -= Q->pj[z-1]->x0; + xy.y -= Q->pj[z-1]->y0; + lp = Q->pj[z-1]->inv(xy, Q->pj[z-1]); + lp.lam += Q->pj[z-1]->lam0; + + switch (z) { + case 1: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN) || + ((lp.lam >= -d40-EPSLN && lp.lam <= -d10+EPSLN) && + (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; + case 2: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN) || + ((lp.lam >= -d180-EPSLN && lp.lam <= -d160+EPSLN) && + (lp.phi >= d50-EPSLN && lp.phi <= d90+EPSLN)) || + ((lp.lam >= -d50-EPSLN && lp.lam <= -d40+EPSLN) && + (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; + case 3: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN); break; + case 4: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN); break; + case 5: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; + case 6: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; + case 7: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; + case 8: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; + case 9: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; + case 10: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; + case 11: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; + case 12: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; + } + z = (!ok? 0: z); /* projectable? */ + } + + if (!z) lp.lam = HUGE_VAL; + if (!z) lp.phi = HUGE_VAL; + + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { + int i; + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + for (i = 0; i < 12; ++i) { + if (static_cast(P->opaque)->pj[i]) + static_cast(P->opaque)->pj[i]->destructor(static_cast(P->opaque)->pj[i], errlev); + } + + return pj_default_destructor(P, errlev); +} + + + +/* + Zones: + + -180 -40 180 + +--------------+-------------------------+ Zones 1,2,9,10,11 & 12: + |1 |2 | Mollweide projection + | | | + +--------------+-------------------------+ Zones 3,4,5,6,7 & 8: + |3 |4 | Sinusoidal projection + | | | + 0 +-------+------+-+-----------+-----------+ + |5 |6 |7 |8 | + | | | | | + +-------+--------+-----------+-----------+ + |9 |10 |11 |12 | + | | | | | + +-------+--------+-----------+-----------+ + -180 -100 -20 80 180 +*/ + +#define SETUP(n, proj, x_0, y_0, lon_0) \ + if (!(Q->pj[n-1] = pj_##proj(0))) return destructor(P, ENOMEM); \ + if (!(Q->pj[n-1] = pj_##proj(Q->pj[n-1]))) return destructor(P, ENOMEM); \ + Q->pj[n-1]->ctx = P->ctx; \ + Q->pj[n-1]->x0 = x_0; \ + Q->pj[n-1]->y0 = y_0; \ + Q->pj[n-1]->lam0 = lon_0; + + +PJ *PROJECTION(igh) { + XY xy1, xy3; + LP lp = { 0, d4044118 }; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + + /* sinusoidal zones */ + SETUP(3, sinu, -d100, 0, -d100); + SETUP(4, sinu, d30, 0, d30); + SETUP(5, sinu, -d160, 0, -d160); + SETUP(6, sinu, -d60, 0, -d60); + SETUP(7, sinu, d20, 0, d20); + SETUP(8, sinu, d140, 0, d140); + + /* mollweide zones */ + SETUP(1, moll, -d100, 0, -d100); + + /* y0 ? */ + xy1 = Q->pj[0]->fwd(lp, Q->pj[0]); /* zone 1 */ + xy3 = Q->pj[2]->fwd(lp, Q->pj[2]); /* zone 3 */ + /* y0 + xy1.y = xy3.y for lt = 40d44'11.8" */ + Q->dy0 = xy3.y - xy1.y; + + Q->pj[0]->y0 = Q->dy0; + + /* mollweide zones (cont'd) */ + SETUP( 2, moll, d30, Q->dy0, d30); + SETUP( 9, moll, -d160, -Q->dy0, -d160); + SETUP(10, moll, -d60, -Q->dy0, -d60); + SETUP(11, moll, d20, -Q->dy0, d20); + SETUP(12, moll, d140, -Q->dy0, d140); + + P->inv = s_inverse; + P->fwd = s_forward; + P->destructor = destructor; + P->es = 0.; + + return P; +} diff --git a/src/PJ_imw_p.c b/src/PJ_imw_p.c deleted file mode 100644 index abed5006..00000000 --- a/src/PJ_imw_p.c +++ /dev/null @@ -1,213 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(imw_p, "International Map of the World Polyconic") - "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]"; - -#define TOL 1e-10 -#define EPS 1e-10 - -enum Mode { - NONE_IS_ZERO = 0, /* phi_1 and phi_2 != 0 */ - PHI_1_IS_ZERO = 1, /* phi_1 = 0 */ - PHI_2_IS_ZERO = -1 /* phi_2 = 0 */ -}; - -struct pj_opaque { - double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2; - double phi_1, phi_2, lam_1; - double *en; - enum Mode mode; -}; - - -static int phi12(PJ *P, double *del, double *sig) { - struct pj_opaque *Q = P->opaque; - int err = 0; - - if (!pj_param(P->ctx, P->params, "tlat_1").i || - !pj_param(P->ctx, P->params, "tlat_2").i) { - err = PJD_ERR_LAT_1_2_UNSPECIFIED; - } else { - Q->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; - Q->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; - *del = 0.5 * (Q->phi_2 - Q->phi_1); - *sig = 0.5 * (Q->phi_2 + Q->phi_1); - err = (fabs(*del) < EPS || fabs(*sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0; - } - return err; -} - - -static XY loc_for(LP lp, PJ *P, double *yc) { - struct pj_opaque *Q = P->opaque; - XY xy; - - if (lp.phi == 0.0) { - xy.x = lp.lam; - xy.y = 0.; - } else { - double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C; - - sp = sin(lp.phi); - m = pj_mlfn(lp.phi, sp, cos(lp.phi), Q->en); - xa = Q->Pp + Q->Qp * m; - ya = Q->P + Q->Q * m; - R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp)); - C = sqrt(R * R - xa * xa); - if (lp.phi < 0.) C = - C; - C += ya - R; - if (Q->mode == PHI_2_IS_ZERO) { - xb = lp.lam; - yb = Q->C2; - } else { - t = lp.lam * Q->sphi_2; - xb = Q->R_2 * sin(t); - yb = Q->C2 + Q->R_2 * (1. - cos(t)); - } - if (Q->mode == PHI_1_IS_ZERO) { - xc = lp.lam; - *yc = 0.; - } else { - t = lp.lam * Q->sphi_1; - xc = Q->R_1 * sin(t); - *yc = Q->R_1 * (1. - cos(t)); - } - D = (xb - xc)/(yb - *yc); - B = xc + D * (C + R - *yc); - xy.x = D * sqrt(R * R * (1 + D * D) - B * B); - if (lp.phi > 0) - xy.x = - xy.x; - xy.x = (B + xy.x) / (1. + D * D); - xy.y = sqrt(R * R - xy.x * xy.x); - if (lp.phi > 0) - xy.y = - xy.y; - xy.y += C + R; - } - return xy; -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - double yc; - XY xy = loc_for(lp, P, &yc); - return (xy); -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - XY t; - double yc = 0.0; - int i = 0; - const int N_MAX_ITER = 1000; /* Arbitrarily chosen number... */ - - lp.phi = Q->phi_2; - lp.lam = xy.x / cos(lp.phi); - do { - t = loc_for(lp, P, &yc); - lp.phi = ((lp.phi - Q->phi_1) * (xy.y - yc) / (t.y - yc)) + Q->phi_1; - lp.lam = lp.lam * xy.x / t.x; - i ++; - } while (i < N_MAX_ITER && - (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL)); - - if( i == N_MAX_ITER ) - { - lp.lam = lp.phi = HUGE_VAL; - } - - return lp; -} - - -static void xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) { - double F; - - *sp = sin(phi); - *R = 1./(tan(phi) * sqrt(1. - P->es * *sp * *sp )); - F = P->opaque->lam_1 * *sp; - *y = *R * (1 - cos(F)); - *x = *R * sin(F); -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - if( P->opaque->en ) - pj_dealloc (P->opaque->en); - - return pj_default_destructor(P, errlev); -} - - -PJ *PROJECTION(imw_p) { - double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2; - int err; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if (!(Q->en = pj_enfn(P->es))) return pj_default_destructor (P, ENOMEM); - if( (err = phi12(P, &del, &sig)) != 0) { - return destructor(P, err); - } - if (Q->phi_2 < Q->phi_1) { /* make sure P->phi_1 most southerly */ - del = Q->phi_1; - Q->phi_1 = Q->phi_2; - Q->phi_2 = del; - } - if (pj_param(P->ctx, P->params, "tlon_1").i) - Q->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; - else { /* use predefined based upon latitude */ - sig = fabs(sig * RAD_TO_DEG); - if (sig <= 60) sig = 2.; - else if (sig <= 76) sig = 4.; - else sig = 8.; - Q->lam_1 = sig * DEG_TO_RAD; - } - Q->mode = NONE_IS_ZERO; - if (Q->phi_1 != 0.0) - xy(P, Q->phi_1, &x1, &y1, &Q->sphi_1, &Q->R_1); - else { - Q->mode = PHI_1_IS_ZERO; - y1 = 0.; - x1 = Q->lam_1; - } - if (Q->phi_2 != 0.0) - xy(P, Q->phi_2, &x2, &T2, &Q->sphi_2, &Q->R_2); - else { - Q->mode = PHI_2_IS_ZERO; - T2 = 0.; - x2 = Q->lam_1; - } - m1 = pj_mlfn(Q->phi_1, Q->sphi_1, cos(Q->phi_1), Q->en); - m2 = pj_mlfn(Q->phi_2, Q->sphi_2, cos(Q->phi_2), Q->en); - t = m2 - m1; - s = x2 - x1; - y2 = sqrt(t * t - s * s) + y1; - Q->C2 = y2 - T2; - t = 1. / t; - Q->P = (m2 * y1 - m1 * y2) * t; - Q->Q = (y2 - y1) * t; - Q->Pp = (m2 * x1 - m1 * x2) * t; - Q->Qp = (x2 - x1) * t; - - P->fwd = e_forward; - P->inv = e_inverse; - P->destructor = destructor; - - return P; -} diff --git a/src/PJ_imw_p.cpp b/src/PJ_imw_p.cpp new file mode 100644 index 00000000..7bf9405a --- /dev/null +++ b/src/PJ_imw_p.cpp @@ -0,0 +1,213 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(imw_p, "International Map of the World Polyconic") + "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]"; + +#define TOL 1e-10 +#define EPS 1e-10 + +enum Mode { + NONE_IS_ZERO = 0, /* phi_1 and phi_2 != 0 */ + PHI_1_IS_ZERO = 1, /* phi_1 = 0 */ + PHI_2_IS_ZERO = -1 /* phi_2 = 0 */ +}; + +struct pj_opaque { + double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2; + double phi_1, phi_2, lam_1; + double *en; + enum Mode mode; +}; + + +static int phi12(PJ *P, double *del, double *sig) { + struct pj_opaque *Q = static_cast(P->opaque); + int err = 0; + + if (!pj_param(P->ctx, P->params, "tlat_1").i || + !pj_param(P->ctx, P->params, "tlat_2").i) { + err = PJD_ERR_LAT_1_2_UNSPECIFIED; + } else { + Q->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; + *del = 0.5 * (Q->phi_2 - Q->phi_1); + *sig = 0.5 * (Q->phi_2 + Q->phi_1); + err = (fabs(*del) < EPS || fabs(*sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0; + } + return err; +} + + +static XY loc_for(LP lp, PJ *P, double *yc) { + struct pj_opaque *Q = static_cast(P->opaque); + XY xy; + + if (lp.phi == 0.0) { + xy.x = lp.lam; + xy.y = 0.; + } else { + double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C; + + sp = sin(lp.phi); + m = pj_mlfn(lp.phi, sp, cos(lp.phi), Q->en); + xa = Q->Pp + Q->Qp * m; + ya = Q->P + Q->Q * m; + R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp)); + C = sqrt(R * R - xa * xa); + if (lp.phi < 0.) C = - C; + C += ya - R; + if (Q->mode == PHI_2_IS_ZERO) { + xb = lp.lam; + yb = Q->C2; + } else { + t = lp.lam * Q->sphi_2; + xb = Q->R_2 * sin(t); + yb = Q->C2 + Q->R_2 * (1. - cos(t)); + } + if (Q->mode == PHI_1_IS_ZERO) { + xc = lp.lam; + *yc = 0.; + } else { + t = lp.lam * Q->sphi_1; + xc = Q->R_1 * sin(t); + *yc = Q->R_1 * (1. - cos(t)); + } + D = (xb - xc)/(yb - *yc); + B = xc + D * (C + R - *yc); + xy.x = D * sqrt(R * R * (1 + D * D) - B * B); + if (lp.phi > 0) + xy.x = - xy.x; + xy.x = (B + xy.x) / (1. + D * D); + xy.y = sqrt(R * R - xy.x * xy.x); + if (lp.phi > 0) + xy.y = - xy.y; + xy.y += C + R; + } + return xy; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + double yc; + XY xy = loc_for(lp, P, &yc); + return (xy); +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + XY t; + double yc = 0.0; + int i = 0; + const int N_MAX_ITER = 1000; /* Arbitrarily chosen number... */ + + lp.phi = Q->phi_2; + lp.lam = xy.x / cos(lp.phi); + do { + t = loc_for(lp, P, &yc); + lp.phi = ((lp.phi - Q->phi_1) * (xy.y - yc) / (t.y - yc)) + Q->phi_1; + lp.lam = lp.lam * xy.x / t.x; + i ++; + } while (i < N_MAX_ITER && + (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL)); + + if( i == N_MAX_ITER ) + { + lp.lam = lp.phi = HUGE_VAL; + } + + return lp; +} + + +static void xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) { + double F; + + *sp = sin(phi); + *R = 1./(tan(phi) * sqrt(1. - P->es * *sp * *sp )); + F = static_cast(P->opaque)->lam_1 * *sp; + *y = *R * (1 - cos(F)); + *x = *R * sin(F); +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + if( static_cast(P->opaque)->en ) + pj_dealloc (static_cast(P->opaque)->en); + + return pj_default_destructor(P, errlev); +} + + +PJ *PROJECTION(imw_p) { + double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2; + int err; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if (!(Q->en = pj_enfn(P->es))) return pj_default_destructor (P, ENOMEM); + if( (err = phi12(P, &del, &sig)) != 0) { + return destructor(P, err); + } + if (Q->phi_2 < Q->phi_1) { /* make sure P->phi_1 most southerly */ + del = Q->phi_1; + Q->phi_1 = Q->phi_2; + Q->phi_2 = del; + } + if (pj_param(P->ctx, P->params, "tlon_1").i) + Q->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; + else { /* use predefined based upon latitude */ + sig = fabs(sig * RAD_TO_DEG); + if (sig <= 60) sig = 2.; + else if (sig <= 76) sig = 4.; + else sig = 8.; + Q->lam_1 = sig * DEG_TO_RAD; + } + Q->mode = NONE_IS_ZERO; + if (Q->phi_1 != 0.0) + xy(P, Q->phi_1, &x1, &y1, &Q->sphi_1, &Q->R_1); + else { + Q->mode = PHI_1_IS_ZERO; + y1 = 0.; + x1 = Q->lam_1; + } + if (Q->phi_2 != 0.0) + xy(P, Q->phi_2, &x2, &T2, &Q->sphi_2, &Q->R_2); + else { + Q->mode = PHI_2_IS_ZERO; + T2 = 0.; + x2 = Q->lam_1; + } + m1 = pj_mlfn(Q->phi_1, Q->sphi_1, cos(Q->phi_1), Q->en); + m2 = pj_mlfn(Q->phi_2, Q->sphi_2, cos(Q->phi_2), Q->en); + t = m2 - m1; + s = x2 - x1; + y2 = sqrt(t * t - s * s) + y1; + Q->C2 = y2 - T2; + t = 1. / t; + Q->P = (m2 * y1 - m1 * y2) * t; + Q->Q = (y2 - y1) * t; + Q->Pp = (m2 * x1 - m1 * x2) * t; + Q->Qp = (x2 - x1) * t; + + P->fwd = e_forward; + P->inv = e_inverse; + P->destructor = destructor; + + return P; +} diff --git a/src/PJ_isea.c b/src/PJ_isea.c deleted file mode 100644 index 4ffd2983..00000000 --- a/src/PJ_isea.c +++ /dev/null @@ -1,1082 +0,0 @@ -/* - * This code was entirely written by Nathan Wagner - * and is in the public domain. - */ - -#include -#include -#include -#include -#include -#include - -#define PJ_LIB__ -#include "proj_internal.h" -#include "proj_math.h" -#include "proj.h" -#include "projects.h" - -#define DEG36 0.62831853071795864768 -#define DEG72 1.25663706143591729537 -#define DEG90 M_PI_2 -#define DEG108 1.88495559215387594306 -#define DEG120 2.09439510239319549229 -#define DEG144 2.51327412287183459075 -#define DEG180 M_PI - -/* sqrt(5)/M_PI */ -#define ISEA_SCALE 0.8301572857837594396028083 - -/* 26.565051177 degrees */ -#define V_LAT 0.46364760899944494524 - -/* 52.62263186 */ -#define E_RAD 0.91843818702186776133 - -/* 10.81231696 */ -#define F_RAD 0.18871053072122403508 - -/* R tan(g) sin(60) */ -#define TABLE_G 0.6615845383 - -/* H = 0.25 R tan g = */ -#define TABLE_H 0.1909830056 - -/* in radians */ -#define ISEA_STD_LAT 1.01722196792335072101 -#define ISEA_STD_LON .19634954084936207740 - -struct hex { - int iso; - long x, y, z; -}; - -/* y *must* be positive down as the xy /iso conversion assumes this */ -static void hex_xy(struct hex *h) { - if (!h->iso) return; - if (h->x >= 0) { - h->y = -h->y - (h->x+1)/2; - } else { - /* need to round toward -inf, not toward zero, so x-1 */ - h->y = -h->y - h->x/2; - } - h->iso = 0; -} - -static void hex_iso(struct hex *h) { - if (h->iso) return; - - if (h->x >= 0) { - h->y = (-h->y - (h->x+1)/2); - } else { - /* need to round toward -inf, not toward zero, so x-1 */ - h->y = (-h->y - (h->x)/2); - } - - h->z = -h->x - h->y; - h->iso = 1; -} - -static void hexbin2(double width, double x, double y, long *i, long *j) { - double z, rx, ry, rz; - double abs_dx, abs_dy, abs_dz; - long ix, iy, iz, s; - struct hex h; - - x = x / cos(30 * M_PI / 180.0); /* rotated X coord */ - y = y - x / 2.0; /* adjustment for rotated X */ - - /* adjust for actual hexwidth */ - x /= width; - y /= width; - - z = -x - y; - - rx = floor(x + 0.5); - ix = lround(rx); - ry = floor(y + 0.5); - iy = lround(ry); - rz = floor(z + 0.5); - iz = lround(rz); - - s = ix + iy + iz; - - if (s) { - abs_dx = fabs(rx - x); - abs_dy = fabs(ry - y); - abs_dz = fabs(rz - z); - - if (abs_dx >= abs_dy && abs_dx >= abs_dz) { - ix -= s; - } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) { - iy -= s; - } else { - iz -= s; - } - } - h.x = ix; - h.y = iy; - h.z = iz; - h.iso = 1; - - hex_xy(&h); - *i = h.x; - *j = h.y; -} - -enum isea_poly { ISEA_NONE, ISEA_ICOSAHEDRON = 20 }; -enum isea_topology { ISEA_HEXAGON=6, ISEA_TRIANGLE=3, ISEA_DIAMOND=4 }; -enum isea_address_form { ISEA_GEO, ISEA_Q2DI, ISEA_SEQNUM, ISEA_INTERLEAVE, - ISEA_PLANE, ISEA_Q2DD, ISEA_PROJTRI, ISEA_VERTEX2DD, ISEA_HEX -}; - -struct isea_dgg { - int polyhedron; /* ignored, icosahedron */ - double o_lat, o_lon, o_az; /* orientation, radians */ - int pole; /* true if standard snyder */ - int topology; /* ignored, hexagon */ - int aperture; /* valid values depend on partitioning method */ - int resolution; - double radius; /* radius of the earth in meters, ignored 1.0 */ - int output; /* an isea_address_form */ - int triangle; /* triangle of last transformed point */ - int quad; /* quad of last transformed point */ - unsigned long serial; -}; - -struct isea_pt { - double x, y; -}; - -struct isea_geo { - double lon, lat; -}; - -/* ENDINC */ - -enum snyder_polyhedron { - SNYDER_POLY_HEXAGON, SNYDER_POLY_PENTAGON, - SNYDER_POLY_TETRAHEDRON, SNYDER_POLY_CUBE, - SNYDER_POLY_OCTAHEDRON, SNYDER_POLY_DODECAHEDRON, - SNYDER_POLY_ICOSAHEDRON -}; - -struct snyder_constants { - double g, G, theta; - /* cppcheck-suppress unusedStructMember */ - double ea_w, ea_a, ea_b, g_w, g_a, g_b; -}; - -/* TODO put these in radians to avoid a later conversion */ -static const struct snyder_constants constants[] = { - {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0}, - {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}, -}; - -static struct isea_geo vertex[] = { - {0.0, DEG90}, - {DEG180, V_LAT}, - {-DEG108, V_LAT}, - {-DEG36, V_LAT}, - {DEG36, V_LAT}, - {DEG108, V_LAT}, - {-DEG144, -V_LAT}, - {-DEG72, -V_LAT}, - {0.0, -V_LAT}, - {DEG72, -V_LAT}, - {DEG144, -V_LAT}, - {0.0, -DEG90} -}; - -/* TODO make an isea_pt array of the vertices as well */ - -static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11}; - -/* triangle Centers */ -static const struct isea_geo icostriangles[] = { - {0.0, 0.0}, - {-DEG144, E_RAD}, - {-DEG72, E_RAD}, - {0.0, E_RAD}, - {DEG72, E_RAD}, - {DEG144, E_RAD}, - {-DEG144, F_RAD}, - {-DEG72, F_RAD}, - {0.0, F_RAD}, - {DEG72, F_RAD}, - {DEG144, F_RAD}, - {-DEG108, -F_RAD}, - {-DEG36, -F_RAD}, - {DEG36, -F_RAD}, - {DEG108, -F_RAD}, - {DEG180, -F_RAD}, - {-DEG108, -E_RAD}, - {-DEG36, -E_RAD}, - {DEG36, -E_RAD}, - {DEG108, -E_RAD}, - {DEG180, -E_RAD}, -}; - -static double az_adjustment(int triangle) -{ - double adj; - - struct isea_geo v; - struct isea_geo c; - - v = vertex[tri_v1[triangle]]; - c = icostriangles[triangle]; - - /* TODO looks like the adjustment is always either 0 or 180 */ - /* at least if you pick your vertex carefully */ - adj = atan2(cos(v.lat) * sin(v.lon - c.lon), - cos(c.lat) * sin(v.lat) - - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon)); - return adj; -} - -static struct isea_pt isea_triangle_xy(int triangle) -{ - struct isea_pt c; - const double Rprime = 0.91038328153090290025; - - triangle = (triangle - 1) % 20; - - c.x = TABLE_G * ((triangle % 5) - 2) * 2.0; - if (triangle > 9) { - c.x += TABLE_G; - } - switch (triangle / 5) { - case 0: - c.y = 5.0 * TABLE_H; - break; - case 1: - c.y = TABLE_H; - break; - case 2: - c.y = -TABLE_H; - break; - case 3: - c.y = -5.0 * TABLE_H; - break; - default: - /* should be impossible */ - exit(EXIT_FAILURE); - }; - c.x *= Rprime; - c.y *= Rprime; - - return c; -} - -/* snyder eq 14 */ -static double sph_azimuth(double f_lon, double f_lat, - double t_lon, double t_lat) -{ - double az; - - az = atan2(cos(t_lat) * sin(t_lon - f_lon), - cos(f_lat) * sin(t_lat) - - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon) - ); - return az; -} - -#ifdef _MSC_VER -#pragma warning( push ) -/* disable unreachable code warning for return 0 */ -#pragma warning( disable : 4702 ) -#endif - -/* coord needs to be in radians */ -static int isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out) -{ - int i; - - /* - * spherical distance from center of polygon face to any of its - * vertexes on the globe - */ - double g; - - /* - * spherical angle between radius vector to center and adjacent edge - * of spherical polygon on the globe - */ - double G; - - /* - * plane angle between radius vector to center and adjacent edge of - * plane polygon - */ - double theta; - - /* additional variables from snyder */ - double q, Rprime, H, Ag, Azprime, Az, dprime, f, rho, - x, y; - - /* variables used to store intermediate results */ - double cot_theta, tan_g, az_offset; - - /* how many multiples of 60 degrees we adjust the azimuth */ - int Az_adjust_multiples; - - struct snyder_constants c; - - /* - * TODO by locality of reference, start by trying the same triangle - * as last time - */ - - /* TODO put these constants in as radians to begin with */ - c = constants[SNYDER_POLY_ICOSAHEDRON]; - theta = PJ_TORAD(c.theta); - g = PJ_TORAD(c.g); - G = PJ_TORAD(c.G); - - for (i = 1; i <= 20; i++) { - double z; - struct isea_geo center; - - center = icostriangles[i]; - - /* step 1 */ - z = acos(sin(center.lat) * sin(ll->lat) - + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon)); - /* not on this triangle */ - if (z > g + 0.000005) { /* TODO DBL_EPSILON */ - continue; - } - - Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat); - - /* step 2 */ - - /* This calculates "some" vertex coordinate */ - az_offset = az_adjustment(i); - - Az -= az_offset; - - /* TODO I don't know why we do this. It's not in snyder */ - /* maybe because we should have picked a better vertex */ - if (Az < 0.0) { - Az += 2.0 * M_PI; - } - /* - * adjust Az for the point to fall within the range of 0 to - * 2(90 - theta) or 60 degrees for the hexagon, by - * and therefore 120 degrees for the triangle - * of the icosahedron - * subtracting or adding multiples of 60 degrees to Az and - * recording the amount of adjustment - */ - - Az_adjust_multiples = 0; - while (Az < 0.0) { - Az += DEG120; - Az_adjust_multiples--; - } - while (Az > DEG120 + DBL_EPSILON) { - Az -= DEG120; - Az_adjust_multiples++; - } - - /* step 3 */ - cot_theta = 1.0 / tan(theta); - tan_g = tan(g); /* TODO this is a constant */ - - /* Calculate q from eq 9. */ - /* TODO cot_theta is cot(30) */ - q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta); - - /* not in this triangle */ - if (z > q + 0.000005) { - continue; - } - /* step 4 */ - - /* Apply equations 5-8 and 10-12 in order */ - - /* eq 5 */ - /* Rprime = 0.9449322893 * R; */ - /* R' in the paper is for the truncated */ - Rprime = 0.91038328153090290025; - - /* eq 6 */ - H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G)); - - /* eq 7 */ - /* Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; */ - Ag = Az + G + H - DEG180; - - /* eq 8 */ - Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta); - - /* eq 10 */ - /* cot(theta) = 1.73205080756887729355 */ - dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta); - - /* eq 11 */ - f = dprime / (2.0 * Rprime * sin(q / 2.0)); - - /* eq 12 */ - rho = 2.0 * Rprime * f * sin(z / 2.0); - - /* - * add back the same 60 degree multiple adjustment from step - * 2 to Azprime - */ - - Azprime += DEG120 * Az_adjust_multiples; - - /* calculate rectangular coordinates */ - - x = rho * sin(Azprime); - y = rho * cos(Azprime); - - /* - * TODO - * translate coordinates to the origin for the particular - * hexagon on the flattened polyhedral map plot - */ - - out->x = x; - out->y = y; - - return i; - } - - /* - * should be impossible, this implies that the coordinate is not on - * any triangle - */ - - fprintf(stderr, "impossible transform: %f %f is not on any triangle\n", - PJ_TODEG(ll->lon), PJ_TODEG(ll->lat)); - - exit(EXIT_FAILURE); - - /* not reached */ - return 0; /* suppresses a warning */ -} - -#ifdef _MSC_VER -#pragma warning( pop ) -#endif - -/* - * return the new coordinates of any point in original coordinate system. - * Define a point (newNPold) in original coordinate system as the North Pole in - * new coordinate system, and the great circle connect the original and new - * North Pole as the lon0 longitude in new coordinate system, given any point - * in original coordinate system, this function return the new coordinates. - */ - -/* formula from Snyder, Map Projections: A working manual, p31 */ -/* - * old north pole at np in new coordinates - * could be simplified a bit with fewer intermediates - * - * TODO take a result pointer - */ -static struct isea_geo snyder_ctran(struct isea_geo * np, struct isea_geo * pt) -{ - struct isea_geo npt; - double alpha, phi, lambda, lambda0, beta, lambdap, phip; - double sin_phip; - double lp_b; /* lambda prime minus beta */ - double cos_p, sin_a; - - phi = pt->lat; - lambda = pt->lon; - alpha = np->lat; - beta = np->lon; - lambda0 = beta; - - cos_p = cos(phi); - sin_a = sin(alpha); - - /* mpawm 5-7 */ - sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0); - - /* mpawm 5-8b */ - - /* use the two argument form so we end up in the right quadrant */ - lp_b = atan2(cos_p * sin(lambda - lambda0), - (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi))); - - lambdap = lp_b + beta; - - /* normalize longitude */ - /* TODO can we just do a modulus ? */ - lambdap = fmod(lambdap, 2 * M_PI); - while (lambdap > M_PI) - lambdap -= 2 * M_PI; - while (lambdap < -M_PI) - lambdap += 2 * M_PI; - - phip = asin(sin_phip); - - npt.lat = phip; - npt.lon = lambdap; - - return npt; -} - -static struct isea_geo isea_ctran(struct isea_geo * np, struct isea_geo * pt, - double lon0) -{ - struct isea_geo npt; - - np->lon += M_PI; - npt = snyder_ctran(np, pt); - np->lon -= M_PI; - - npt.lon -= (M_PI - lon0 + np->lon); - - /* - * snyder is down tri 3, isea is along side of tri1 from vertex 0 to - * vertex 1 these are 180 degrees apart - */ - npt.lon += M_PI; - /* normalize longitude */ - npt.lon = fmod(npt.lon, 2 * M_PI); - while (npt.lon > M_PI) - npt.lon -= 2 * M_PI; - while (npt.lon < -M_PI) - npt.lon += 2 * M_PI; - - return npt; -} - -/* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */ - -static int isea_grid_init(struct isea_dgg * g) -{ - if (!g) - return 0; - - g->polyhedron = 20; - g->o_lat = ISEA_STD_LAT; - g->o_lon = ISEA_STD_LON; - g->o_az = 0.0; - g->aperture = 4; - g->resolution = 6; - g->radius = 1.0; - g->topology = 6; - - return 1; -} - -static void isea_orient_isea(struct isea_dgg * g) -{ - if (!g) - return; - g->o_lat = ISEA_STD_LAT; - g->o_lon = ISEA_STD_LON; - g->o_az = 0.0; -} - -static void isea_orient_pole(struct isea_dgg * g) -{ - if (!g) - return; - g->o_lat = M_PI / 2.0; - g->o_lon = 0.0; - g->o_az = 0; -} - -static int isea_transform(struct isea_dgg * g, struct isea_geo * in, - struct isea_pt * out) -{ - struct isea_geo i, pole; - int tri; - - pole.lat = g->o_lat; - pole.lon = g->o_lon; - - i = isea_ctran(&pole, in, g->o_az); - - tri = isea_snyder_forward(&i, out); - out->x *= g->radius; - out->y *= g->radius; - g->triangle = tri; - - return tri; -} - -#define DOWNTRI(tri) (((tri - 1) / 5) % 2 == 1) - -static void isea_rotate(struct isea_pt * pt, double degrees) -{ - double rad; - - double x, y; - - rad = -degrees * M_PI / 180.0; - while (rad >= 2.0 * M_PI) rad -= 2.0 * M_PI; - while (rad <= -2.0 * M_PI) rad += 2.0 * M_PI; - - x = pt->x * cos(rad) + pt->y * sin(rad); - y = -pt->x * sin(rad) + pt->y * cos(rad); - - pt->x = x; - pt->y = y; -} - -static int isea_tri_plane(int tri, struct isea_pt *pt, double radius) { - struct isea_pt tc; /* center of triangle */ - - if (DOWNTRI(tri)) { - isea_rotate(pt, 180.0); - } - tc = isea_triangle_xy(tri); - tc.x *= radius; - tc.y *= radius; - pt->x += tc.x; - pt->y += tc.y; - - return tri; -} - -/* convert projected triangle coords to quad xy coords, return quad number */ -static int isea_ptdd(int tri, struct isea_pt *pt) { - int downtri, quad; - - downtri = (((tri - 1) / 5) % 2 == 1); - quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1; - - isea_rotate(pt, downtri ? 240.0 : 60.0); - if (downtri) { - pt->x += 0.5; - /* pt->y += cos(30.0 * M_PI / 180.0); */ - pt->y += .86602540378443864672; - } - return quad; -} - -static int isea_dddi_ap3odd(struct isea_dgg *g, int quad, struct isea_pt *pt, - struct isea_pt *di) -{ - struct isea_pt v; - double hexwidth; - double sidelength; /* in hexes */ - long d, i; - long maxcoord; - struct hex h; - - /* This is the number of hexes from apex to base of a triangle */ - sidelength = (pow(2.0, g->resolution) + 1.0) / 2.0; - - /* apex to base is cos(30deg) */ - hexwidth = cos(M_PI / 6.0) / sidelength; - - /* TODO I think sidelength is always x.5, so - * (int)sidelength * 2 + 1 might be just as good - */ - maxcoord = lround((sidelength * 2.0)); - - v = *pt; - hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); - h.iso = 0; - hex_iso(&h); - - d = h.x - h.z; - i = h.x + h.y + h.y; - - /* - * you want to test for max coords for the next quad in the same - * "row" first to get the case where both are max - */ - if (quad <= 5) { - if (d == 0 && i == maxcoord) { - /* north pole */ - quad = 0; - d = 0; - i = 0; - } else if (i == maxcoord) { - /* upper right in next quad */ - quad += 1; - if (quad == 6) - quad = 1; - i = maxcoord - d; - d = 0; - } else if (d == maxcoord) { - /* lower right in quad to lower right */ - quad += 5; - d = 0; - } - } else if (quad >= 6) { - if (i == 0 && d == maxcoord) { - /* south pole */ - quad = 11; - d = 0; - i = 0; - } else if (d == maxcoord) { - /* lower right in next quad */ - quad += 1; - if (quad == 11) - quad = 6; - d = maxcoord - i; - i = 0; - } else if (i == maxcoord) { - /* upper right in quad to upper right */ - quad = (quad - 4) % 5; - i = 0; - } - } - - di->x = d; - di->y = i; - - g->quad = quad; - return quad; -} - -static int isea_dddi(struct isea_dgg *g, int quad, struct isea_pt *pt, - struct isea_pt *di) { - struct isea_pt v; - double hexwidth; - long sidelength; /* in hexes */ - struct hex h; - - if (g->aperture == 3 && g->resolution % 2 != 0) { - return isea_dddi_ap3odd(g, quad, pt, di); - } - /* todo might want to do this as an iterated loop */ - if (g->aperture >0) { - sidelength = lround(pow(g->aperture, g->resolution / 2.0)); - } else { - sidelength = g->resolution; - } - - hexwidth = 1.0 / sidelength; - - v = *pt; - isea_rotate(&v, -30.0); - hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); - h.iso = 0; - hex_iso(&h); - - /* we may actually be on another quad */ - if (quad <= 5) { - if (h.x == 0 && h.z == -sidelength) { - /* north pole */ - quad = 0; - h.z = 0; - h.y = 0; - h.x = 0; - } else if (h.z == -sidelength) { - quad = quad + 1; - if (quad == 6) - quad = 1; - h.y = sidelength - h.x; - h.z = h.x - sidelength; - h.x = 0; - } else if (h.x == sidelength) { - quad += 5; - h.y = -h.z; - h.x = 0; - } - } else if (quad >= 6) { - if (h.z == 0 && h.x == sidelength) { - /* south pole */ - quad = 11; - h.x = 0; - h.y = 0; - h.z = 0; - } else if (h.x == sidelength) { - quad = quad + 1; - if (quad == 11) - quad = 6; - h.x = h.y + sidelength; - h.y = 0; - h.z = -h.x; - } else if (h.y == -sidelength) { - quad -= 4; - h.y = 0; - h.z = -h.x; - } - } - di->x = h.x; - di->y = -h.z; - - g->quad = quad; - return quad; -} - -static int isea_ptdi(struct isea_dgg *g, int tri, struct isea_pt *pt, - struct isea_pt *di) { - struct isea_pt v; - int quad; - - v = *pt; - quad = isea_ptdd(tri, &v); - quad = isea_dddi(g, quad, &v, di); - return quad; -} - -/* q2di to seqnum */ - -static long isea_disn(struct isea_dgg *g, int quad, struct isea_pt *di) { - long sidelength; - long sn, height; - long hexes; - - if (quad == 0) { - g->serial = 1; - return g->serial; - } - /* hexes in a quad */ - hexes = lround(pow(g->aperture, g->resolution)); - if (quad == 11) { - g->serial = 1 + 10 * hexes + 1; - return g->serial; - } - if (g->aperture == 3 && g->resolution % 2 == 1) { - height = lround(floor((pow(g->aperture, (g->resolution - 1) / 2.0)))); - sn = ((long)di->x) * height; - sn += ((long)di->y) / height; - sn += (quad - 1) * hexes; - sn += 2; - } else { - sidelength = lround((pow(g->aperture, g->resolution / 2.0))); - sn = lround(floor(((quad - 1) * hexes + sidelength * di->x + di->y + 2))); - } - - g->serial = sn; - return sn; -} - -/* TODO just encode the quad in the d or i coordinate - * quad is 0-11, which can be four bits. - * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf - */ -/* convert a q2di to global hex coord */ -static int isea_hex(struct isea_dgg *g, int tri, - struct isea_pt *pt, struct isea_pt *hex) { - struct isea_pt v; -#ifdef FIXME - long sidelength; - long d, i, x, y; -#endif - int quad; - - quad = isea_ptdi(g, tri, pt, &v); - - hex->x = ((int)v.x << 4) + quad; - hex->y = v.y; - - return 1; -#ifdef FIXME - d = lround(floor(v.x)); - i = lround(floor(v.y)); - - /* Aperture 3 odd resolutions */ - if (g->aperture == 3 && g->resolution % 2 != 0) { - long offset = lround((pow(3.0, g->resolution - 1) + 0.5)); - - d += offset * ((g->quad-1) % 5); - i += offset * ((g->quad-1) % 5); - - if (quad == 0) { - d = 0; - i = offset; - } else if (quad == 11) { - d = 2 * offset; - i = 0; - } else if (quad > 5) { - d += offset; - } - - x = (2*d - i) /3; - y = (2*i - d) /3; - - hex->x = x + offset / 3; - hex->y = y + 2 * offset / 3; - return 1; - } - - /* aperture 3 even resolutions and aperture 4 */ - sidelength = lround((pow(g->aperture, g->resolution / 2.0))); - if (g->quad == 0) { - hex->x = 0; - hex->y = sidelength; - } else if (g->quad == 11) { - hex->x = sidelength * 2; - hex->y = 0; - } else { - hex->x = d + sidelength * ((g->quad-1) % 5); - if (g->quad > 5) hex->x += sidelength; - hex->y = i + sidelength * ((g->quad-1) % 5); - } - - return 1; -#endif -} - -static struct isea_pt isea_forward(struct isea_dgg *g, struct isea_geo *in) -{ - int tri; - struct isea_pt out, coord; - - tri = isea_transform(g, in, &out); - - if (g->output == ISEA_PLANE) { - isea_tri_plane(tri, &out, g->radius); - return out; - } - - /* convert to isea standard triangle size */ - out.x = out.x / g->radius * ISEA_SCALE; - out.y = out.y / g->radius * ISEA_SCALE; - out.x += 0.5; - out.y += 2.0 * .14433756729740644112; - - switch (g->output) { - case ISEA_PROJTRI: - /* nothing to do, already in projected triangle */ - break; - case ISEA_VERTEX2DD: - g->quad = isea_ptdd(tri, &out); - break; - case ISEA_Q2DD: - /* Same as above, we just don't print as much */ - g->quad = isea_ptdd(tri, &out); - break; - case ISEA_Q2DI: - g->quad = isea_ptdi(g, tri, &out, &coord); - return coord; - break; - case ISEA_SEQNUM: - isea_ptdi(g, tri, &out, &coord); - /* disn will set g->serial */ - isea_disn(g, g->quad, &coord); - return coord; - break; - case ISEA_HEX: - isea_hex(g, tri, &out, &coord); - return coord; - break; - } - - return out; -} - -/* - * Proj 4 integration code follows - */ - -PROJ_HEAD(isea, "Icosahedral Snyder Equal Area") "\n\tSph"; - -struct pj_opaque { - struct isea_dgg dgg; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - struct isea_pt out; - struct isea_geo in; - - in.lon = lp.lam; - in.lat = lp.phi; - - out = isea_forward(&Q->dgg, &in); - - xy.x = out.x; - xy.y = out.y; - - return xy; -} - - -PJ *PROJECTION(isea) { - char *opt; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - - P->fwd = s_forward; - isea_grid_init(&Q->dgg); - - Q->dgg.output = ISEA_PLANE; -/* P->dgg.radius = P->a; / * otherwise defaults to 1 */ - /* calling library will scale, I think */ - - opt = pj_param(P->ctx,P->params, "sorient").s; - if (opt) { - if (!strcmp(opt, "isea")) { - isea_orient_isea(&Q->dgg); - } else if (!strcmp(opt, "pole")) { - isea_orient_pole(&Q->dgg); - } else { - return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); - } - } - - if (pj_param(P->ctx,P->params, "tazi").i) { - Q->dgg.o_az = pj_param(P->ctx,P->params, "razi").f; - } - - if (pj_param(P->ctx,P->params, "tlon_0").i) { - Q->dgg.o_lon = pj_param(P->ctx,P->params, "rlon_0").f; - } - - if (pj_param(P->ctx,P->params, "tlat_0").i) { - Q->dgg.o_lat = pj_param(P->ctx,P->params, "rlat_0").f; - } - - if (pj_param(P->ctx,P->params, "taperture").i) { - Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; - } - - if (pj_param(P->ctx,P->params, "tresolution").i) { - Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; - } - - opt = pj_param(P->ctx,P->params, "smode").s; - if (opt) { - if (!strcmp(opt, "plane")) { - Q->dgg.output = ISEA_PLANE; - } else if (!strcmp(opt, "di")) { - Q->dgg.output = ISEA_Q2DI; - } - else if (!strcmp(opt, "dd")) { - Q->dgg.output = ISEA_Q2DD; - } - else if (!strcmp(opt, "hex")) { - Q->dgg.output = ISEA_HEX; - } - else { - /* TODO verify error code. Possibly eliminate magic */ - return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); - } - } - - if (pj_param(P->ctx,P->params, "trescale").i) { - Q->dgg.radius = ISEA_SCALE; - } - - if (pj_param(P->ctx,P->params, "tresolution").i) { - Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; - } else { - Q->dgg.resolution = 4; - } - - if (pj_param(P->ctx,P->params, "taperture").i) { - Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; - } else { - Q->dgg.aperture = 3; - } - - return P; -} diff --git a/src/PJ_isea.cpp b/src/PJ_isea.cpp new file mode 100644 index 00000000..6170b8a1 --- /dev/null +++ b/src/PJ_isea.cpp @@ -0,0 +1,1082 @@ +/* + * This code was entirely written by Nathan Wagner + * and is in the public domain. + */ + +#include +#include +#include +#include +#include +#include + +#define PJ_LIB__ +#include "proj_internal.h" +#include "proj_math.h" +#include "proj.h" +#include "projects.h" + +#define DEG36 0.62831853071795864768 +#define DEG72 1.25663706143591729537 +#define DEG90 M_PI_2 +#define DEG108 1.88495559215387594306 +#define DEG120 2.09439510239319549229 +#define DEG144 2.51327412287183459075 +#define DEG180 M_PI + +/* sqrt(5)/M_PI */ +#define ISEA_SCALE 0.8301572857837594396028083 + +/* 26.565051177 degrees */ +#define V_LAT 0.46364760899944494524 + +/* 52.62263186 */ +#define E_RAD 0.91843818702186776133 + +/* 10.81231696 */ +#define F_RAD 0.18871053072122403508 + +/* R tan(g) sin(60) */ +#define TABLE_G 0.6615845383 + +/* H = 0.25 R tan g = */ +#define TABLE_H 0.1909830056 + +/* in radians */ +#define ISEA_STD_LAT 1.01722196792335072101 +#define ISEA_STD_LON .19634954084936207740 + +struct hex { + int iso; + long x, y, z; +}; + +/* y *must* be positive down as the xy /iso conversion assumes this */ +static void hex_xy(struct hex *h) { + if (!h->iso) return; + if (h->x >= 0) { + h->y = -h->y - (h->x+1)/2; + } else { + /* need to round toward -inf, not toward zero, so x-1 */ + h->y = -h->y - h->x/2; + } + h->iso = 0; +} + +static void hex_iso(struct hex *h) { + if (h->iso) return; + + if (h->x >= 0) { + h->y = (-h->y - (h->x+1)/2); + } else { + /* need to round toward -inf, not toward zero, so x-1 */ + h->y = (-h->y - (h->x)/2); + } + + h->z = -h->x - h->y; + h->iso = 1; +} + +static void hexbin2(double width, double x, double y, long *i, long *j) { + double z, rx, ry, rz; + double abs_dx, abs_dy, abs_dz; + long ix, iy, iz, s; + struct hex h; + + x = x / cos(30 * M_PI / 180.0); /* rotated X coord */ + y = y - x / 2.0; /* adjustment for rotated X */ + + /* adjust for actual hexwidth */ + x /= width; + y /= width; + + z = -x - y; + + rx = floor(x + 0.5); + ix = lround(rx); + ry = floor(y + 0.5); + iy = lround(ry); + rz = floor(z + 0.5); + iz = lround(rz); + + s = ix + iy + iz; + + if (s) { + abs_dx = fabs(rx - x); + abs_dy = fabs(ry - y); + abs_dz = fabs(rz - z); + + if (abs_dx >= abs_dy && abs_dx >= abs_dz) { + ix -= s; + } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) { + iy -= s; + } else { + iz -= s; + } + } + h.x = ix; + h.y = iy; + h.z = iz; + h.iso = 1; + + hex_xy(&h); + *i = h.x; + *j = h.y; +} + +enum isea_poly { ISEA_NONE, ISEA_ICOSAHEDRON = 20 }; +enum isea_topology { ISEA_HEXAGON=6, ISEA_TRIANGLE=3, ISEA_DIAMOND=4 }; +enum isea_address_form { ISEA_GEO, ISEA_Q2DI, ISEA_SEQNUM, ISEA_INTERLEAVE, + ISEA_PLANE, ISEA_Q2DD, ISEA_PROJTRI, ISEA_VERTEX2DD, ISEA_HEX +}; + +struct isea_dgg { + int polyhedron; /* ignored, icosahedron */ + double o_lat, o_lon, o_az; /* orientation, radians */ + int pole; /* true if standard snyder */ + int topology; /* ignored, hexagon */ + int aperture; /* valid values depend on partitioning method */ + int resolution; + double radius; /* radius of the earth in meters, ignored 1.0 */ + int output; /* an isea_address_form */ + int triangle; /* triangle of last transformed point */ + int quad; /* quad of last transformed point */ + unsigned long serial; +}; + +struct isea_pt { + double x, y; +}; + +struct isea_geo { + double lon, lat; +}; + +/* ENDINC */ + +enum snyder_polyhedron { + SNYDER_POLY_HEXAGON, SNYDER_POLY_PENTAGON, + SNYDER_POLY_TETRAHEDRON, SNYDER_POLY_CUBE, + SNYDER_POLY_OCTAHEDRON, SNYDER_POLY_DODECAHEDRON, + SNYDER_POLY_ICOSAHEDRON +}; + +struct snyder_constants { + double g, G, theta; + /* cppcheck-suppress unusedStructMember */ + double ea_w, ea_a, ea_b, g_w, g_a, g_b; +}; + +/* TODO put these in radians to avoid a later conversion */ +static const struct snyder_constants constants[] = { + {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0}, + {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, + {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}, +}; + +static struct isea_geo vertex[] = { + {0.0, DEG90}, + {DEG180, V_LAT}, + {-DEG108, V_LAT}, + {-DEG36, V_LAT}, + {DEG36, V_LAT}, + {DEG108, V_LAT}, + {-DEG144, -V_LAT}, + {-DEG72, -V_LAT}, + {0.0, -V_LAT}, + {DEG72, -V_LAT}, + {DEG144, -V_LAT}, + {0.0, -DEG90} +}; + +/* TODO make an isea_pt array of the vertices as well */ + +static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11}; + +/* triangle Centers */ +static const struct isea_geo icostriangles[] = { + {0.0, 0.0}, + {-DEG144, E_RAD}, + {-DEG72, E_RAD}, + {0.0, E_RAD}, + {DEG72, E_RAD}, + {DEG144, E_RAD}, + {-DEG144, F_RAD}, + {-DEG72, F_RAD}, + {0.0, F_RAD}, + {DEG72, F_RAD}, + {DEG144, F_RAD}, + {-DEG108, -F_RAD}, + {-DEG36, -F_RAD}, + {DEG36, -F_RAD}, + {DEG108, -F_RAD}, + {DEG180, -F_RAD}, + {-DEG108, -E_RAD}, + {-DEG36, -E_RAD}, + {DEG36, -E_RAD}, + {DEG108, -E_RAD}, + {DEG180, -E_RAD}, +}; + +static double az_adjustment(int triangle) +{ + double adj; + + struct isea_geo v; + struct isea_geo c; + + v = vertex[tri_v1[triangle]]; + c = icostriangles[triangle]; + + /* TODO looks like the adjustment is always either 0 or 180 */ + /* at least if you pick your vertex carefully */ + adj = atan2(cos(v.lat) * sin(v.lon - c.lon), + cos(c.lat) * sin(v.lat) + - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon)); + return adj; +} + +static struct isea_pt isea_triangle_xy(int triangle) +{ + struct isea_pt c; + const double Rprime = 0.91038328153090290025; + + triangle = (triangle - 1) % 20; + + c.x = TABLE_G * ((triangle % 5) - 2) * 2.0; + if (triangle > 9) { + c.x += TABLE_G; + } + switch (triangle / 5) { + case 0: + c.y = 5.0 * TABLE_H; + break; + case 1: + c.y = TABLE_H; + break; + case 2: + c.y = -TABLE_H; + break; + case 3: + c.y = -5.0 * TABLE_H; + break; + default: + /* should be impossible */ + exit(EXIT_FAILURE); + }; + c.x *= Rprime; + c.y *= Rprime; + + return c; +} + +/* snyder eq 14 */ +static double sph_azimuth(double f_lon, double f_lat, + double t_lon, double t_lat) +{ + double az; + + az = atan2(cos(t_lat) * sin(t_lon - f_lon), + cos(f_lat) * sin(t_lat) + - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon) + ); + return az; +} + +#ifdef _MSC_VER +#pragma warning( push ) +/* disable unreachable code warning for return 0 */ +#pragma warning( disable : 4702 ) +#endif + +/* coord needs to be in radians */ +static int isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out) +{ + int i; + + /* + * spherical distance from center of polygon face to any of its + * vertexes on the globe + */ + double g; + + /* + * spherical angle between radius vector to center and adjacent edge + * of spherical polygon on the globe + */ + double G; + + /* + * plane angle between radius vector to center and adjacent edge of + * plane polygon + */ + double theta; + + /* additional variables from snyder */ + double q, Rprime, H, Ag, Azprime, Az, dprime, f, rho, + x, y; + + /* variables used to store intermediate results */ + double cot_theta, tan_g, az_offset; + + /* how many multiples of 60 degrees we adjust the azimuth */ + int Az_adjust_multiples; + + struct snyder_constants c; + + /* + * TODO by locality of reference, start by trying the same triangle + * as last time + */ + + /* TODO put these constants in as radians to begin with */ + c = constants[SNYDER_POLY_ICOSAHEDRON]; + theta = PJ_TORAD(c.theta); + g = PJ_TORAD(c.g); + G = PJ_TORAD(c.G); + + for (i = 1; i <= 20; i++) { + double z; + struct isea_geo center; + + center = icostriangles[i]; + + /* step 1 */ + z = acos(sin(center.lat) * sin(ll->lat) + + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon)); + /* not on this triangle */ + if (z > g + 0.000005) { /* TODO DBL_EPSILON */ + continue; + } + + Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat); + + /* step 2 */ + + /* This calculates "some" vertex coordinate */ + az_offset = az_adjustment(i); + + Az -= az_offset; + + /* TODO I don't know why we do this. It's not in snyder */ + /* maybe because we should have picked a better vertex */ + if (Az < 0.0) { + Az += 2.0 * M_PI; + } + /* + * adjust Az for the point to fall within the range of 0 to + * 2(90 - theta) or 60 degrees for the hexagon, by + * and therefore 120 degrees for the triangle + * of the icosahedron + * subtracting or adding multiples of 60 degrees to Az and + * recording the amount of adjustment + */ + + Az_adjust_multiples = 0; + while (Az < 0.0) { + Az += DEG120; + Az_adjust_multiples--; + } + while (Az > DEG120 + DBL_EPSILON) { + Az -= DEG120; + Az_adjust_multiples++; + } + + /* step 3 */ + cot_theta = 1.0 / tan(theta); + tan_g = tan(g); /* TODO this is a constant */ + + /* Calculate q from eq 9. */ + /* TODO cot_theta is cot(30) */ + q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta); + + /* not in this triangle */ + if (z > q + 0.000005) { + continue; + } + /* step 4 */ + + /* Apply equations 5-8 and 10-12 in order */ + + /* eq 5 */ + /* Rprime = 0.9449322893 * R; */ + /* R' in the paper is for the truncated */ + Rprime = 0.91038328153090290025; + + /* eq 6 */ + H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G)); + + /* eq 7 */ + /* Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; */ + Ag = Az + G + H - DEG180; + + /* eq 8 */ + Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta); + + /* eq 10 */ + /* cot(theta) = 1.73205080756887729355 */ + dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta); + + /* eq 11 */ + f = dprime / (2.0 * Rprime * sin(q / 2.0)); + + /* eq 12 */ + rho = 2.0 * Rprime * f * sin(z / 2.0); + + /* + * add back the same 60 degree multiple adjustment from step + * 2 to Azprime + */ + + Azprime += DEG120 * Az_adjust_multiples; + + /* calculate rectangular coordinates */ + + x = rho * sin(Azprime); + y = rho * cos(Azprime); + + /* + * TODO + * translate coordinates to the origin for the particular + * hexagon on the flattened polyhedral map plot + */ + + out->x = x; + out->y = y; + + return i; + } + + /* + * should be impossible, this implies that the coordinate is not on + * any triangle + */ + + fprintf(stderr, "impossible transform: %f %f is not on any triangle\n", + PJ_TODEG(ll->lon), PJ_TODEG(ll->lat)); + + exit(EXIT_FAILURE); + + /* not reached */ + return 0; /* suppresses a warning */ +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +/* + * return the new coordinates of any point in original coordinate system. + * Define a point (newNPold) in original coordinate system as the North Pole in + * new coordinate system, and the great circle connect the original and new + * North Pole as the lon0 longitude in new coordinate system, given any point + * in original coordinate system, this function return the new coordinates. + */ + +/* formula from Snyder, Map Projections: A working manual, p31 */ +/* + * old north pole at np in new coordinates + * could be simplified a bit with fewer intermediates + * + * TODO take a result pointer + */ +static struct isea_geo snyder_ctran(struct isea_geo * np, struct isea_geo * pt) +{ + struct isea_geo npt; + double alpha, phi, lambda, lambda0, beta, lambdap, phip; + double sin_phip; + double lp_b; /* lambda prime minus beta */ + double cos_p, sin_a; + + phi = pt->lat; + lambda = pt->lon; + alpha = np->lat; + beta = np->lon; + lambda0 = beta; + + cos_p = cos(phi); + sin_a = sin(alpha); + + /* mpawm 5-7 */ + sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0); + + /* mpawm 5-8b */ + + /* use the two argument form so we end up in the right quadrant */ + lp_b = atan2(cos_p * sin(lambda - lambda0), + (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi))); + + lambdap = lp_b + beta; + + /* normalize longitude */ + /* TODO can we just do a modulus ? */ + lambdap = fmod(lambdap, 2 * M_PI); + while (lambdap > M_PI) + lambdap -= 2 * M_PI; + while (lambdap < -M_PI) + lambdap += 2 * M_PI; + + phip = asin(sin_phip); + + npt.lat = phip; + npt.lon = lambdap; + + return npt; +} + +static struct isea_geo isea_ctran(struct isea_geo * np, struct isea_geo * pt, + double lon0) +{ + struct isea_geo npt; + + np->lon += M_PI; + npt = snyder_ctran(np, pt); + np->lon -= M_PI; + + npt.lon -= (M_PI - lon0 + np->lon); + + /* + * snyder is down tri 3, isea is along side of tri1 from vertex 0 to + * vertex 1 these are 180 degrees apart + */ + npt.lon += M_PI; + /* normalize longitude */ + npt.lon = fmod(npt.lon, 2 * M_PI); + while (npt.lon > M_PI) + npt.lon -= 2 * M_PI; + while (npt.lon < -M_PI) + npt.lon += 2 * M_PI; + + return npt; +} + +/* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */ + +static int isea_grid_init(struct isea_dgg * g) +{ + if (!g) + return 0; + + g->polyhedron = 20; + g->o_lat = ISEA_STD_LAT; + g->o_lon = ISEA_STD_LON; + g->o_az = 0.0; + g->aperture = 4; + g->resolution = 6; + g->radius = 1.0; + g->topology = 6; + + return 1; +} + +static void isea_orient_isea(struct isea_dgg * g) +{ + if (!g) + return; + g->o_lat = ISEA_STD_LAT; + g->o_lon = ISEA_STD_LON; + g->o_az = 0.0; +} + +static void isea_orient_pole(struct isea_dgg * g) +{ + if (!g) + return; + g->o_lat = M_PI / 2.0; + g->o_lon = 0.0; + g->o_az = 0; +} + +static int isea_transform(struct isea_dgg * g, struct isea_geo * in, + struct isea_pt * out) +{ + struct isea_geo i, pole; + int tri; + + pole.lat = g->o_lat; + pole.lon = g->o_lon; + + i = isea_ctran(&pole, in, g->o_az); + + tri = isea_snyder_forward(&i, out); + out->x *= g->radius; + out->y *= g->radius; + g->triangle = tri; + + return tri; +} + +#define DOWNTRI(tri) (((tri - 1) / 5) % 2 == 1) + +static void isea_rotate(struct isea_pt * pt, double degrees) +{ + double rad; + + double x, y; + + rad = -degrees * M_PI / 180.0; + while (rad >= 2.0 * M_PI) rad -= 2.0 * M_PI; + while (rad <= -2.0 * M_PI) rad += 2.0 * M_PI; + + x = pt->x * cos(rad) + pt->y * sin(rad); + y = -pt->x * sin(rad) + pt->y * cos(rad); + + pt->x = x; + pt->y = y; +} + +static int isea_tri_plane(int tri, struct isea_pt *pt, double radius) { + struct isea_pt tc; /* center of triangle */ + + if (DOWNTRI(tri)) { + isea_rotate(pt, 180.0); + } + tc = isea_triangle_xy(tri); + tc.x *= radius; + tc.y *= radius; + pt->x += tc.x; + pt->y += tc.y; + + return tri; +} + +/* convert projected triangle coords to quad xy coords, return quad number */ +static int isea_ptdd(int tri, struct isea_pt *pt) { + int downtri, quad; + + downtri = (((tri - 1) / 5) % 2 == 1); + quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1; + + isea_rotate(pt, downtri ? 240.0 : 60.0); + if (downtri) { + pt->x += 0.5; + /* pt->y += cos(30.0 * M_PI / 180.0); */ + pt->y += .86602540378443864672; + } + return quad; +} + +static int isea_dddi_ap3odd(struct isea_dgg *g, int quad, struct isea_pt *pt, + struct isea_pt *di) +{ + struct isea_pt v; + double hexwidth; + double sidelength; /* in hexes */ + long d, i; + long maxcoord; + struct hex h; + + /* This is the number of hexes from apex to base of a triangle */ + sidelength = (pow(2.0, g->resolution) + 1.0) / 2.0; + + /* apex to base is cos(30deg) */ + hexwidth = cos(M_PI / 6.0) / sidelength; + + /* TODO I think sidelength is always x.5, so + * (int)sidelength * 2 + 1 might be just as good + */ + maxcoord = lround((sidelength * 2.0)); + + v = *pt; + hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); + h.iso = 0; + hex_iso(&h); + + d = h.x - h.z; + i = h.x + h.y + h.y; + + /* + * you want to test for max coords for the next quad in the same + * "row" first to get the case where both are max + */ + if (quad <= 5) { + if (d == 0 && i == maxcoord) { + /* north pole */ + quad = 0; + d = 0; + i = 0; + } else if (i == maxcoord) { + /* upper right in next quad */ + quad += 1; + if (quad == 6) + quad = 1; + i = maxcoord - d; + d = 0; + } else if (d == maxcoord) { + /* lower right in quad to lower right */ + quad += 5; + d = 0; + } + } else if (quad >= 6) { + if (i == 0 && d == maxcoord) { + /* south pole */ + quad = 11; + d = 0; + i = 0; + } else if (d == maxcoord) { + /* lower right in next quad */ + quad += 1; + if (quad == 11) + quad = 6; + d = maxcoord - i; + i = 0; + } else if (i == maxcoord) { + /* upper right in quad to upper right */ + quad = (quad - 4) % 5; + i = 0; + } + } + + di->x = d; + di->y = i; + + g->quad = quad; + return quad; +} + +static int isea_dddi(struct isea_dgg *g, int quad, struct isea_pt *pt, + struct isea_pt *di) { + struct isea_pt v; + double hexwidth; + long sidelength; /* in hexes */ + struct hex h; + + if (g->aperture == 3 && g->resolution % 2 != 0) { + return isea_dddi_ap3odd(g, quad, pt, di); + } + /* todo might want to do this as an iterated loop */ + if (g->aperture >0) { + sidelength = lround(pow(g->aperture, g->resolution / 2.0)); + } else { + sidelength = g->resolution; + } + + hexwidth = 1.0 / sidelength; + + v = *pt; + isea_rotate(&v, -30.0); + hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); + h.iso = 0; + hex_iso(&h); + + /* we may actually be on another quad */ + if (quad <= 5) { + if (h.x == 0 && h.z == -sidelength) { + /* north pole */ + quad = 0; + h.z = 0; + h.y = 0; + h.x = 0; + } else if (h.z == -sidelength) { + quad = quad + 1; + if (quad == 6) + quad = 1; + h.y = sidelength - h.x; + h.z = h.x - sidelength; + h.x = 0; + } else if (h.x == sidelength) { + quad += 5; + h.y = -h.z; + h.x = 0; + } + } else if (quad >= 6) { + if (h.z == 0 && h.x == sidelength) { + /* south pole */ + quad = 11; + h.x = 0; + h.y = 0; + h.z = 0; + } else if (h.x == sidelength) { + quad = quad + 1; + if (quad == 11) + quad = 6; + h.x = h.y + sidelength; + h.y = 0; + h.z = -h.x; + } else if (h.y == -sidelength) { + quad -= 4; + h.y = 0; + h.z = -h.x; + } + } + di->x = h.x; + di->y = -h.z; + + g->quad = quad; + return quad; +} + +static int isea_ptdi(struct isea_dgg *g, int tri, struct isea_pt *pt, + struct isea_pt *di) { + struct isea_pt v; + int quad; + + v = *pt; + quad = isea_ptdd(tri, &v); + quad = isea_dddi(g, quad, &v, di); + return quad; +} + +/* q2di to seqnum */ + +static long isea_disn(struct isea_dgg *g, int quad, struct isea_pt *di) { + long sidelength; + long sn, height; + long hexes; + + if (quad == 0) { + g->serial = 1; + return g->serial; + } + /* hexes in a quad */ + hexes = lround(pow(g->aperture, g->resolution)); + if (quad == 11) { + g->serial = 1 + 10 * hexes + 1; + return g->serial; + } + if (g->aperture == 3 && g->resolution % 2 == 1) { + height = lround(floor((pow(g->aperture, (g->resolution - 1) / 2.0)))); + sn = ((long)di->x) * height; + sn += ((long)di->y) / height; + sn += (quad - 1) * hexes; + sn += 2; + } else { + sidelength = lround((pow(g->aperture, g->resolution / 2.0))); + sn = lround(floor(((quad - 1) * hexes + sidelength * di->x + di->y + 2))); + } + + g->serial = sn; + return sn; +} + +/* TODO just encode the quad in the d or i coordinate + * quad is 0-11, which can be four bits. + * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf + */ +/* convert a q2di to global hex coord */ +static int isea_hex(struct isea_dgg *g, int tri, + struct isea_pt *pt, struct isea_pt *hex) { + struct isea_pt v; +#ifdef FIXME + long sidelength; + long d, i, x, y; +#endif + int quad; + + quad = isea_ptdi(g, tri, pt, &v); + + hex->x = ((int)v.x << 4) + quad; + hex->y = v.y; + + return 1; +#ifdef FIXME + d = lround(floor(v.x)); + i = lround(floor(v.y)); + + /* Aperture 3 odd resolutions */ + if (g->aperture == 3 && g->resolution % 2 != 0) { + long offset = lround((pow(3.0, g->resolution - 1) + 0.5)); + + d += offset * ((g->quad-1) % 5); + i += offset * ((g->quad-1) % 5); + + if (quad == 0) { + d = 0; + i = offset; + } else if (quad == 11) { + d = 2 * offset; + i = 0; + } else if (quad > 5) { + d += offset; + } + + x = (2*d - i) /3; + y = (2*i - d) /3; + + hex->x = x + offset / 3; + hex->y = y + 2 * offset / 3; + return 1; + } + + /* aperture 3 even resolutions and aperture 4 */ + sidelength = lround((pow(g->aperture, g->resolution / 2.0))); + if (g->quad == 0) { + hex->x = 0; + hex->y = sidelength; + } else if (g->quad == 11) { + hex->x = sidelength * 2; + hex->y = 0; + } else { + hex->x = d + sidelength * ((g->quad-1) % 5); + if (g->quad > 5) hex->x += sidelength; + hex->y = i + sidelength * ((g->quad-1) % 5); + } + + return 1; +#endif +} + +static struct isea_pt isea_forward(struct isea_dgg *g, struct isea_geo *in) +{ + int tri; + struct isea_pt out, coord; + + tri = isea_transform(g, in, &out); + + if (g->output == ISEA_PLANE) { + isea_tri_plane(tri, &out, g->radius); + return out; + } + + /* convert to isea standard triangle size */ + out.x = out.x / g->radius * ISEA_SCALE; + out.y = out.y / g->radius * ISEA_SCALE; + out.x += 0.5; + out.y += 2.0 * .14433756729740644112; + + switch (g->output) { + case ISEA_PROJTRI: + /* nothing to do, already in projected triangle */ + break; + case ISEA_VERTEX2DD: + g->quad = isea_ptdd(tri, &out); + break; + case ISEA_Q2DD: + /* Same as above, we just don't print as much */ + g->quad = isea_ptdd(tri, &out); + break; + case ISEA_Q2DI: + g->quad = isea_ptdi(g, tri, &out, &coord); + return coord; + break; + case ISEA_SEQNUM: + isea_ptdi(g, tri, &out, &coord); + /* disn will set g->serial */ + isea_disn(g, g->quad, &coord); + return coord; + break; + case ISEA_HEX: + isea_hex(g, tri, &out, &coord); + return coord; + break; + } + + return out; +} + +/* + * Proj 4 integration code follows + */ + +PROJ_HEAD(isea, "Icosahedral Snyder Equal Area") "\n\tSph"; + +struct pj_opaque { + struct isea_dgg dgg; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + struct isea_pt out; + struct isea_geo in; + + in.lon = lp.lam; + in.lat = lp.phi; + + out = isea_forward(&Q->dgg, &in); + + xy.x = out.x; + xy.y = out.y; + + return xy; +} + + +PJ *PROJECTION(isea) { + char *opt; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + + P->fwd = s_forward; + isea_grid_init(&Q->dgg); + + Q->dgg.output = ISEA_PLANE; +/* P->dgg.radius = P->a; / * otherwise defaults to 1 */ + /* calling library will scale, I think */ + + opt = pj_param(P->ctx,P->params, "sorient").s; + if (opt) { + if (!strcmp(opt, "isea")) { + isea_orient_isea(&Q->dgg); + } else if (!strcmp(opt, "pole")) { + isea_orient_pole(&Q->dgg); + } else { + return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); + } + } + + if (pj_param(P->ctx,P->params, "tazi").i) { + Q->dgg.o_az = pj_param(P->ctx,P->params, "razi").f; + } + + if (pj_param(P->ctx,P->params, "tlon_0").i) { + Q->dgg.o_lon = pj_param(P->ctx,P->params, "rlon_0").f; + } + + if (pj_param(P->ctx,P->params, "tlat_0").i) { + Q->dgg.o_lat = pj_param(P->ctx,P->params, "rlat_0").f; + } + + if (pj_param(P->ctx,P->params, "taperture").i) { + Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; + } + + if (pj_param(P->ctx,P->params, "tresolution").i) { + Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; + } + + opt = pj_param(P->ctx,P->params, "smode").s; + if (opt) { + if (!strcmp(opt, "plane")) { + Q->dgg.output = ISEA_PLANE; + } else if (!strcmp(opt, "di")) { + Q->dgg.output = ISEA_Q2DI; + } + else if (!strcmp(opt, "dd")) { + Q->dgg.output = ISEA_Q2DD; + } + else if (!strcmp(opt, "hex")) { + Q->dgg.output = ISEA_HEX; + } + else { + /* TODO verify error code. Possibly eliminate magic */ + return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); + } + } + + if (pj_param(P->ctx,P->params, "trescale").i) { + Q->dgg.radius = ISEA_SCALE; + } + + if (pj_param(P->ctx,P->params, "tresolution").i) { + Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; + } else { + Q->dgg.resolution = 4; + } + + if (pj_param(P->ctx,P->params, "taperture").i) { + Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; + } else { + Q->dgg.aperture = 3; + } + + return P; +} diff --git a/src/PJ_krovak.c b/src/PJ_krovak.c deleted file mode 100644 index 5ca21214..00000000 --- a/src/PJ_krovak.c +++ /dev/null @@ -1,220 +0,0 @@ - /* - * Project: PROJ - * Purpose: Implementation of the krovak (Krovak) projection. - * Definition: http://www.ihsenergy.com/epsg/guid7.html#1.4.3 - * Author: Thomas Flemming, tf@ttqv.com - * - ****************************************************************************** - * Copyright (c) 2001, Thomas Flemming, tf@ttqv.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. - ****************************************************************************** - * A description of the (forward) projection is found in: - * - * Bohuslav Veverka, - * - * KROVAK’S PROJECTION AND ITS USE FOR THE - * CZECH REPUBLIC AND THE SLOVAK REPUBLIC, - * - * 50 years of the Research Institute of - * and the Slovak Republic Geodesy, Topography and Cartography - * - * which can be found via the Wayback Machine: - * - * https://web.archive.org/web/20150216143806/https://www.vugtk.cz/odis/sborniky/sb2005/Sbornik_50_let_VUGTK/Part_1-Scientific_Contribution/16-Veverka.pdf - * - * Further info, including the inverse projection, is given by EPSG: - * - * Guidance Note 7 part 2 - * Coordinate Conversions and Transformations including Formulas - * - * http://www.iogp.org/pubs/373-07-2.pdf - * - * Variable names in this file mostly follows what is used in the - * paper by Veverka. - * - * According to EPSG the full Krovak projection method should have - * the following parameters. Within PROJ the azimuth, and pseudo - * standard parallel are hardcoded in the algorithm and can't be - * altered from outside. The others all have defaults to match the - * common usage with Krovak projection. - * - * lat_0 = latitude of centre of the projection - * - * lon_0 = longitude of centre of the projection - * - * ** = azimuth (true) of the centre line passing through the - * centre of the projection - * - * ** = latitude of pseudo standard parallel - * - * k = scale factor on the pseudo standard parallel - * - * x_0 = False Easting of the centre of the projection at the - * apex of the cone - * - * y_0 = False Northing of the centre of the projection at - * the apex of the cone - * - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(krovak, "Krovak") "\n\tPCyl, Ell"; - -#define EPS 1e-15 -#define UQ 1.04216856380474 /* DU(2, 59, 42, 42.69689) */ -#define S0 1.37008346281555 /* Latitude of pseudo standard parallel 78deg 30'00" N */ -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - -struct pj_opaque { - double alpha; - double k; - double n; - double rho0; - double ad; - int czech; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - struct pj_opaque *Q = P->opaque; - XY xy = {0.0,0.0}; - - double gfi, u, deltav, s, d, eps, rho; - - gfi = pow ( (1. + P->e * sin(lp.phi)) / (1. - P->e * sin(lp.phi)), Q->alpha * P->e / 2.); - - u = 2. * (atan(Q->k * pow( tan(lp.phi / 2. + M_PI_4), Q->alpha) / gfi)-M_PI_4); - deltav = -lp.lam * Q->alpha; - - s = asin(cos(Q->ad) * sin(u) + sin(Q->ad) * cos(u) * cos(deltav)); - d = asin(cos(u) * sin(deltav) / cos(s)); - - eps = Q->n * d; - rho = Q->rho0 * pow(tan(S0 / 2. + M_PI_4) , Q->n) / pow(tan(s / 2. + M_PI_4) , Q->n); - - xy.y = rho * cos(eps); - xy.x = rho * sin(eps); - - xy.y *= Q->czech; - xy.x *= Q->czech; - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - struct pj_opaque *Q = P->opaque; - LP lp = {0.0,0.0}; - - double u, deltav, s, d, eps, rho, fi1, xy0; - int i; - - xy0 = xy.x; - xy.x = xy.y; - xy.y = xy0; - - xy.x *= Q->czech; - xy.y *= Q->czech; - - rho = sqrt(xy.x * xy.x + xy.y * xy.y); - eps = atan2(xy.y, xy.x); - - d = eps / sin(S0); - s = 2. * (atan( pow(Q->rho0 / rho, 1. / Q->n) * tan(S0 / 2. + M_PI_4)) - M_PI_4); - - u = asin(cos(Q->ad) * sin(s) - sin(Q->ad) * cos(s) * cos(d)); - deltav = asin(cos(s) * sin(d) / cos(u)); - - lp.lam = P->lam0 - deltav / Q->alpha; - - /* ITERATION FOR lp.phi */ - fi1 = u; - - for (i = MAX_ITER; i ; --i) { - lp.phi = 2. * ( atan( pow( Q->k, -1. / Q->alpha) * - pow( tan(u / 2. + M_PI_4) , 1. / Q->alpha) * - pow( (1. + P->e * sin(fi1)) / (1. - P->e * sin(fi1)) , P->e / 2.) - ) - M_PI_4); - - if (fabs(fi1 - lp.phi) < EPS) - break; - fi1 = lp.phi; - } - if( i == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - - lp.lam -= P->lam0; - - return lp; -} - - -PJ *PROJECTION(krovak) { - double u0, n0, g; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - /* we want Bessel as fixed ellipsoid */ - P->a = 6377397.155; - P->e = sqrt(P->es = 0.006674372230614); - - /* if latitude of projection center is not set, use 49d30'N */ - if (!pj_param(P->ctx, P->params, "tlat_0").i) - P->phi0 = 0.863937979737193; - - /* if center long is not set use 42d30'E of Ferro - 17d40' for Ferro */ - /* that will correspond to using longitudes relative to greenwich */ - /* as input and output, instead of lat/long relative to Ferro */ - if (!pj_param(P->ctx, P->params, "tlon_0").i) - P->lam0 = 0.7417649320975901 - 0.308341501185665; - - /* if scale not set default to 0.9999 */ - if (!pj_param(P->ctx, P->params, "tk").i && !pj_param(P->ctx, P->params, "tk_0").i) - P->k0 = 0.9999; - - Q->czech = 1; - if( !pj_param(P->ctx, P->params, "tczech").i ) - Q->czech = -1; - - /* Set up shared parameters between forward and inverse */ - Q->alpha = sqrt(1. + (P->es * pow(cos(P->phi0), 4)) / (1. - P->es)); - u0 = asin(sin(P->phi0) / Q->alpha); - g = pow( (1. + P->e * sin(P->phi0)) / (1. - P->e * sin(P->phi0)) , Q->alpha * P->e / 2. ); - Q->k = tan( u0 / 2. + M_PI_4) / pow (tan(P->phi0 / 2. + M_PI_4) , Q->alpha) * g; - n0 = sqrt(1. - P->es) / (1. - P->es * pow(sin(P->phi0), 2)); - Q->n = sin(S0); - Q->rho0 = P->k0 * n0 / tan(S0); - Q->ad = M_PI_2 - UQ; - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_krovak.cpp b/src/PJ_krovak.cpp new file mode 100644 index 00000000..7e0488f0 --- /dev/null +++ b/src/PJ_krovak.cpp @@ -0,0 +1,220 @@ + /* + * Project: PROJ + * Purpose: Implementation of the krovak (Krovak) projection. + * Definition: http://www.ihsenergy.com/epsg/guid7.html#1.4.3 + * Author: Thomas Flemming, tf@ttqv.com + * + ****************************************************************************** + * Copyright (c) 2001, Thomas Flemming, tf@ttqv.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. + ****************************************************************************** + * A description of the (forward) projection is found in: + * + * Bohuslav Veverka, + * + * KROVAK’S PROJECTION AND ITS USE FOR THE + * CZECH REPUBLIC AND THE SLOVAK REPUBLIC, + * + * 50 years of the Research Institute of + * and the Slovak Republic Geodesy, Topography and Cartography + * + * which can be found via the Wayback Machine: + * + * https://web.archive.org/web/20150216143806/https://www.vugtk.cz/odis/sborniky/sb2005/Sbornik_50_let_VUGTK/Part_1-Scientific_Contribution/16-Veverka.pdf + * + * Further info, including the inverse projection, is given by EPSG: + * + * Guidance Note 7 part 2 + * Coordinate Conversions and Transformations including Formulas + * + * http://www.iogp.org/pubs/373-07-2.pdf + * + * Variable names in this file mostly follows what is used in the + * paper by Veverka. + * + * According to EPSG the full Krovak projection method should have + * the following parameters. Within PROJ the azimuth, and pseudo + * standard parallel are hardcoded in the algorithm and can't be + * altered from outside. The others all have defaults to match the + * common usage with Krovak projection. + * + * lat_0 = latitude of centre of the projection + * + * lon_0 = longitude of centre of the projection + * + * ** = azimuth (true) of the centre line passing through the + * centre of the projection + * + * ** = latitude of pseudo standard parallel + * + * k = scale factor on the pseudo standard parallel + * + * x_0 = False Easting of the centre of the projection at the + * apex of the cone + * + * y_0 = False Northing of the centre of the projection at + * the apex of the cone + * + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(krovak, "Krovak") "\n\tPCyl, Ell"; + +#define EPS 1e-15 +#define UQ 1.04216856380474 /* DU(2, 59, 42, 42.69689) */ +#define S0 1.37008346281555 /* Latitude of pseudo standard parallel 78deg 30'00" N */ +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + +struct pj_opaque { + double alpha; + double k; + double n; + double rho0; + double ad; + int czech; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + struct pj_opaque *Q = static_cast(P->opaque); + XY xy = {0.0,0.0}; + + double gfi, u, deltav, s, d, eps, rho; + + gfi = pow ( (1. + P->e * sin(lp.phi)) / (1. - P->e * sin(lp.phi)), Q->alpha * P->e / 2.); + + u = 2. * (atan(Q->k * pow( tan(lp.phi / 2. + M_PI_4), Q->alpha) / gfi)-M_PI_4); + deltav = -lp.lam * Q->alpha; + + s = asin(cos(Q->ad) * sin(u) + sin(Q->ad) * cos(u) * cos(deltav)); + d = asin(cos(u) * sin(deltav) / cos(s)); + + eps = Q->n * d; + rho = Q->rho0 * pow(tan(S0 / 2. + M_PI_4) , Q->n) / pow(tan(s / 2. + M_PI_4) , Q->n); + + xy.y = rho * cos(eps); + xy.x = rho * sin(eps); + + xy.y *= Q->czech; + xy.x *= Q->czech; + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + struct pj_opaque *Q = static_cast(P->opaque); + LP lp = {0.0,0.0}; + + double u, deltav, s, d, eps, rho, fi1, xy0; + int i; + + xy0 = xy.x; + xy.x = xy.y; + xy.y = xy0; + + xy.x *= Q->czech; + xy.y *= Q->czech; + + rho = sqrt(xy.x * xy.x + xy.y * xy.y); + eps = atan2(xy.y, xy.x); + + d = eps / sin(S0); + s = 2. * (atan( pow(Q->rho0 / rho, 1. / Q->n) * tan(S0 / 2. + M_PI_4)) - M_PI_4); + + u = asin(cos(Q->ad) * sin(s) - sin(Q->ad) * cos(s) * cos(d)); + deltav = asin(cos(s) * sin(d) / cos(u)); + + lp.lam = P->lam0 - deltav / Q->alpha; + + /* ITERATION FOR lp.phi */ + fi1 = u; + + for (i = MAX_ITER; i ; --i) { + lp.phi = 2. * ( atan( pow( Q->k, -1. / Q->alpha) * + pow( tan(u / 2. + M_PI_4) , 1. / Q->alpha) * + pow( (1. + P->e * sin(fi1)) / (1. - P->e * sin(fi1)) , P->e / 2.) + ) - M_PI_4); + + if (fabs(fi1 - lp.phi) < EPS) + break; + fi1 = lp.phi; + } + if( i == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + + lp.lam -= P->lam0; + + return lp; +} + + +PJ *PROJECTION(krovak) { + double u0, n0, g; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + /* we want Bessel as fixed ellipsoid */ + P->a = 6377397.155; + P->e = sqrt(P->es = 0.006674372230614); + + /* if latitude of projection center is not set, use 49d30'N */ + if (!pj_param(P->ctx, P->params, "tlat_0").i) + P->phi0 = 0.863937979737193; + + /* if center long is not set use 42d30'E of Ferro - 17d40' for Ferro */ + /* that will correspond to using longitudes relative to greenwich */ + /* as input and output, instead of lat/long relative to Ferro */ + if (!pj_param(P->ctx, P->params, "tlon_0").i) + P->lam0 = 0.7417649320975901 - 0.308341501185665; + + /* if scale not set default to 0.9999 */ + if (!pj_param(P->ctx, P->params, "tk").i && !pj_param(P->ctx, P->params, "tk_0").i) + P->k0 = 0.9999; + + Q->czech = 1; + if( !pj_param(P->ctx, P->params, "tczech").i ) + Q->czech = -1; + + /* Set up shared parameters between forward and inverse */ + Q->alpha = sqrt(1. + (P->es * pow(cos(P->phi0), 4)) / (1. - P->es)); + u0 = asin(sin(P->phi0) / Q->alpha); + g = pow( (1. + P->e * sin(P->phi0)) / (1. - P->e * sin(P->phi0)) , Q->alpha * P->e / 2. ); + Q->k = tan( u0 / 2. + M_PI_4) / pow (tan(P->phi0 / 2. + M_PI_4) , Q->alpha) * g; + n0 = sqrt(1. - P->es) / (1. - P->es * pow(sin(P->phi0), 2)); + Q->n = sin(S0); + Q->rho0 = P->k0 * n0 / tan(S0); + Q->ad = M_PI_2 - UQ; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_labrd.c b/src/PJ_labrd.c deleted file mode 100644 index 769dc620..00000000 --- a/src/PJ_labrd.c +++ /dev/null @@ -1,130 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(labrd, "Laborde") "\n\tCyl, Sph\n\tSpecial for Madagascar"; -#define EPS 1.e-10 - -struct pj_opaque { - double kRg, p0s, A, C, Ca, Cb, Cc, Cd; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double V1, V2, ps, sinps, cosps, sinps2, cosps2; - double I1, I2, I3, I4, I5, I6, x2, y2, t; - - V1 = Q->A * log( tan(M_FORTPI + .5 * lp.phi) ); - t = P->e * sin(lp.phi); - V2 = .5 * P->e * Q->A * log ((1. + t)/(1. - t)); - ps = 2. * (atan(exp(V1 - V2 + Q->C)) - M_FORTPI); - I1 = ps - Q->p0s; - cosps = cos(ps); cosps2 = cosps * cosps; - sinps = sin(ps); sinps2 = sinps * sinps; - I4 = Q->A * cosps; - I2 = .5 * Q->A * I4 * sinps; - I3 = I2 * Q->A * Q->A * (5. * cosps2 - sinps2) / 12.; - I6 = I4 * Q->A * Q->A; - I5 = I6 * (cosps2 - sinps2) / 6.; - I6 *= Q->A * Q->A * - (5. * cosps2 * cosps2 + sinps2 * (sinps2 - 18. * cosps2)) / 120.; - t = lp.lam * lp.lam; - xy.x = Q->kRg * lp.lam * (I4 + t * (I5 + t * I6)); - xy.y = Q->kRg * (I1 + t * (I2 + t * I3)); - x2 = xy.x * xy.x; - y2 = xy.y * xy.y; - V1 = 3. * xy.x * y2 - xy.x * x2; - V2 = xy.y * y2 - 3. * x2 * xy.y; - xy.x += Q->Ca * V1 + Q->Cb * V2; - xy.y += Q->Ca * V2 - Q->Cb * V1; - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - /* t = 0.0 optimization is to avoid a false positive cppcheck warning */ - /* (cppcheck git beaf29c15867984aa3c2a15cf15bd7576ccde2b3). Might no */ - /* longer be necessary with later versions. */ - double x2, y2, V1, V2, V3, V4, t = 0.0, t2, ps, pe, tpe, s; - double I7, I8, I9, I10, I11, d, Re; - int i; - - x2 = xy.x * xy.x; - y2 = xy.y * xy.y; - V1 = 3. * xy.x * y2 - xy.x * x2; - V2 = xy.y * y2 - 3. * x2 * xy.y; - V3 = xy.x * (5. * y2 * y2 + x2 * (-10. * y2 + x2 )); - V4 = xy.y * (5. * x2 * x2 + y2 * (-10. * x2 + y2 )); - xy.x += - Q->Ca * V1 - Q->Cb * V2 + Q->Cc * V3 + Q->Cd * V4; - xy.y += Q->Cb * V1 - Q->Ca * V2 - Q->Cd * V3 + Q->Cc * V4; - ps = Q->p0s + xy.y / Q->kRg; - pe = ps + P->phi0 - Q->p0s; - - for ( i = 20; i; --i) { - V1 = Q->A * log(tan(M_FORTPI + .5 * pe)); - tpe = P->e * sin(pe); - V2 = .5 * P->e * Q->A * log((1. + tpe)/(1. - tpe)); - t = ps - 2. * (atan(exp(V1 - V2 + Q->C)) - M_FORTPI); - pe += t; - if (fabs(t) < EPS) - break; - } - - t = P->e * sin(pe); - t = 1. - t * t; - Re = P->one_es / ( t * sqrt(t) ); - t = tan(ps); - t2 = t * t; - s = Q->kRg * Q->kRg; - d = Re * P->k0 * Q->kRg; - I7 = t / (2. * d); - I8 = t * (5. + 3. * t2) / (24. * d * s); - d = cos(ps) * Q->kRg * Q->A; - I9 = 1. / d; - d *= s; - I10 = (1. + 2. * t2) / (6. * d); - I11 = (5. + t2 * (28. + 24. * t2)) / (120. * d * s); - x2 = xy.x * xy.x; - lp.phi = pe + x2 * (-I7 + I8 * x2); - lp.lam = xy.x * (I9 + x2 * (-I10 + x2 * I11)); - return lp; -} - - -PJ *PROJECTION(labrd) { - double Az, sinp, R, N, t; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Az = pj_param(P->ctx, P->params, "razi").f; - sinp = sin(P->phi0); - t = 1. - P->es * sinp * sinp; - N = 1. / sqrt(t); - R = P->one_es * N / t; - Q->kRg = P->k0 * sqrt( N * R ); - Q->p0s = atan( sqrt(R / N) * tan(P->phi0) ); - Q->A = sinp / sin(Q->p0s); - t = P->e * sinp; - Q->C = .5 * P->e * Q->A * log((1. + t)/(1. - t)) + - - Q->A * log( tan(M_FORTPI + .5 * P->phi0)) - + log( tan(M_FORTPI + .5 * Q->p0s)); - t = Az + Az; - Q->Ca = (1. - cos(t)) * ( Q->Cb = 1. / (12. * Q->kRg * Q->kRg) ); - Q->Cb *= sin(t); - Q->Cc = 3. * (Q->Ca * Q->Ca - Q->Cb * Q->Cb); - Q->Cd = 6. * Q->Ca * Q->Cb; - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_labrd.cpp b/src/PJ_labrd.cpp new file mode 100644 index 00000000..e40bbcbd --- /dev/null +++ b/src/PJ_labrd.cpp @@ -0,0 +1,130 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(labrd, "Laborde") "\n\tCyl, Sph\n\tSpecial for Madagascar"; +#define EPS 1.e-10 + +struct pj_opaque { + double kRg, p0s, A, C, Ca, Cb, Cc, Cd; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double V1, V2, ps, sinps, cosps, sinps2, cosps2; + double I1, I2, I3, I4, I5, I6, x2, y2, t; + + V1 = Q->A * log( tan(M_FORTPI + .5 * lp.phi) ); + t = P->e * sin(lp.phi); + V2 = .5 * P->e * Q->A * log ((1. + t)/(1. - t)); + ps = 2. * (atan(exp(V1 - V2 + Q->C)) - M_FORTPI); + I1 = ps - Q->p0s; + cosps = cos(ps); cosps2 = cosps * cosps; + sinps = sin(ps); sinps2 = sinps * sinps; + I4 = Q->A * cosps; + I2 = .5 * Q->A * I4 * sinps; + I3 = I2 * Q->A * Q->A * (5. * cosps2 - sinps2) / 12.; + I6 = I4 * Q->A * Q->A; + I5 = I6 * (cosps2 - sinps2) / 6.; + I6 *= Q->A * Q->A * + (5. * cosps2 * cosps2 + sinps2 * (sinps2 - 18. * cosps2)) / 120.; + t = lp.lam * lp.lam; + xy.x = Q->kRg * lp.lam * (I4 + t * (I5 + t * I6)); + xy.y = Q->kRg * (I1 + t * (I2 + t * I3)); + x2 = xy.x * xy.x; + y2 = xy.y * xy.y; + V1 = 3. * xy.x * y2 - xy.x * x2; + V2 = xy.y * y2 - 3. * x2 * xy.y; + xy.x += Q->Ca * V1 + Q->Cb * V2; + xy.y += Q->Ca * V2 - Q->Cb * V1; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + /* t = 0.0 optimization is to avoid a false positive cppcheck warning */ + /* (cppcheck git beaf29c15867984aa3c2a15cf15bd7576ccde2b3). Might no */ + /* longer be necessary with later versions. */ + double x2, y2, V1, V2, V3, V4, t = 0.0, t2, ps, pe, tpe, s; + double I7, I8, I9, I10, I11, d, Re; + int i; + + x2 = xy.x * xy.x; + y2 = xy.y * xy.y; + V1 = 3. * xy.x * y2 - xy.x * x2; + V2 = xy.y * y2 - 3. * x2 * xy.y; + V3 = xy.x * (5. * y2 * y2 + x2 * (-10. * y2 + x2 )); + V4 = xy.y * (5. * x2 * x2 + y2 * (-10. * x2 + y2 )); + xy.x += - Q->Ca * V1 - Q->Cb * V2 + Q->Cc * V3 + Q->Cd * V4; + xy.y += Q->Cb * V1 - Q->Ca * V2 - Q->Cd * V3 + Q->Cc * V4; + ps = Q->p0s + xy.y / Q->kRg; + pe = ps + P->phi0 - Q->p0s; + + for ( i = 20; i; --i) { + V1 = Q->A * log(tan(M_FORTPI + .5 * pe)); + tpe = P->e * sin(pe); + V2 = .5 * P->e * Q->A * log((1. + tpe)/(1. - tpe)); + t = ps - 2. * (atan(exp(V1 - V2 + Q->C)) - M_FORTPI); + pe += t; + if (fabs(t) < EPS) + break; + } + + t = P->e * sin(pe); + t = 1. - t * t; + Re = P->one_es / ( t * sqrt(t) ); + t = tan(ps); + t2 = t * t; + s = Q->kRg * Q->kRg; + d = Re * P->k0 * Q->kRg; + I7 = t / (2. * d); + I8 = t * (5. + 3. * t2) / (24. * d * s); + d = cos(ps) * Q->kRg * Q->A; + I9 = 1. / d; + d *= s; + I10 = (1. + 2. * t2) / (6. * d); + I11 = (5. + t2 * (28. + 24. * t2)) / (120. * d * s); + x2 = xy.x * xy.x; + lp.phi = pe + x2 * (-I7 + I8 * x2); + lp.lam = xy.x * (I9 + x2 * (-I10 + x2 * I11)); + return lp; +} + + +PJ *PROJECTION(labrd) { + double Az, sinp, R, N, t; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Az = pj_param(P->ctx, P->params, "razi").f; + sinp = sin(P->phi0); + t = 1. - P->es * sinp * sinp; + N = 1. / sqrt(t); + R = P->one_es * N / t; + Q->kRg = P->k0 * sqrt( N * R ); + Q->p0s = atan( sqrt(R / N) * tan(P->phi0) ); + Q->A = sinp / sin(Q->p0s); + t = P->e * sinp; + Q->C = .5 * P->e * Q->A * log((1. + t)/(1. - t)) + + - Q->A * log( tan(M_FORTPI + .5 * P->phi0)) + + log( tan(M_FORTPI + .5 * Q->p0s)); + t = Az + Az; + Q->Ca = (1. - cos(t)) * ( Q->Cb = 1. / (12. * Q->kRg * Q->kRg) ); + Q->Cb *= sin(t); + Q->Cc = 3. * (Q->Ca * Q->Ca - Q->Cb * Q->Cb); + Q->Cd = 6. * Q->Ca * Q->Cb; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_laea.c b/src/PJ_laea.c deleted file mode 100644 index bcf9c44d..00000000 --- a/src/PJ_laea.c +++ /dev/null @@ -1,296 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(laea, "Lambert Azimuthal Equal Area") "\n\tAzi, Sph&Ell"; - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double sinb1; - double cosb1; - double xmf; - double ymf; - double mmf; - double qp; - double dd; - double rq; - double *apa; - enum Mode mode; -}; - -#define EPS10 1.e-10 - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, sinlam, sinphi, q, sinb=0.0, cosb=0.0, b=0.0; - - coslam = cos(lp.lam); - sinlam = sin(lp.lam); - sinphi = sin(lp.phi); - q = pj_qsfn(sinphi, P->e, P->one_es); - - if (Q->mode == OBLIQ || Q->mode == EQUIT) { - sinb = q / Q->qp; - cosb = sqrt(1. - sinb * sinb); - } - - switch (Q->mode) { - case OBLIQ: - b = 1. + Q->sinb1 * sinb + Q->cosb1 * cosb * coslam; - break; - case EQUIT: - b = 1. + cosb * coslam; - break; - case N_POLE: - b = M_HALFPI + lp.phi; - q = Q->qp - q; - break; - case S_POLE: - b = lp.phi - M_HALFPI; - q = Q->qp + q; - break; - } - if (fabs(b) < EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - switch (Q->mode) { - case OBLIQ: - b = sqrt(2. / b); - xy.y = Q->ymf * b * (Q->cosb1 * sinb - Q->sinb1 * cosb * coslam); - goto eqcon; - break; - case EQUIT: - b = sqrt(2. / (1. + cosb * coslam)); - xy.y = b * sinb * Q->ymf; -eqcon: - xy.x = Q->xmf * b * cosb * sinlam; - break; - case N_POLE: - case S_POLE: - if (q >= 0.) { - b = sqrt(q); - xy.x = b * sinlam; - xy.y = coslam * (Q->mode == S_POLE ? b : -b); - } else - xy.x = xy.y = 0.; - break; - } - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (Q->mode) { - case EQUIT: - xy.y = 1. + cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = 1. + Q->sinb1 * sinphi + Q->cosb1 * cosphi * coslam; -oblcon: - if (xy.y <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.y = sqrt(2. / xy.y); - xy.x = xy.y * cosphi * sin(lp.lam); - xy.y *= Q->mode == EQUIT ? sinphi : - Q->cosb1 * sinphi - Q->sinb1 * cosphi * coslam; - break; - case N_POLE: - coslam = -coslam; - /*-fallthrough*/ - case S_POLE: - if (fabs(lp.phi + P->phi0) < EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.y = M_FORTPI - lp.phi * .5; - xy.y = 2. * (Q->mode == S_POLE ? cos(xy.y) : sin(xy.y)); - xy.x = xy.y * sin(lp.lam); - xy.y *= coslam; - break; - } - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cCe, sCe, q, rho, ab=0.0; - - switch (Q->mode) { - case EQUIT: - case OBLIQ: - xy.x /= Q->dd; - xy.y *= Q->dd; - rho = hypot(xy.x, xy.y); - if (rho < EPS10) { - lp.lam = 0.; - lp.phi = P->phi0; - return lp; - } - sCe = 2. * asin(.5 * rho / Q->rq); - cCe = cos(sCe); - sCe = sin(sCe); - xy.x *= sCe; - if (Q->mode == OBLIQ) { - ab = cCe * Q->sinb1 + xy.y * sCe * Q->cosb1 / rho; - xy.y = rho * Q->cosb1 * cCe - xy.y * Q->sinb1 * sCe; - } else { - ab = xy.y * sCe / rho; - xy.y = rho * cCe; - } - break; - case N_POLE: - xy.y = -xy.y; - /*-fallthrough*/ - case S_POLE: - q = (xy.x * xy.x + xy.y * xy.y); - if (q == 0.0) { - lp.lam = 0.; - lp.phi = P->phi0; - return (lp); - } - ab = 1. - q / Q->qp; - if (Q->mode == S_POLE) - ab = - ab; - break; - } - lp.lam = atan2(xy.x, xy.y); - lp.phi = pj_authlat(asin(ab), Q->apa); - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosz=0.0, rh, sinz=0.0; - - rh = hypot(xy.x, xy.y); - if ((lp.phi = rh * .5 ) > 1.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - lp.phi = 2. * asin(lp.phi); - if (Q->mode == OBLIQ || Q->mode == EQUIT) { - sinz = sin(lp.phi); - cosz = cos(lp.phi); - } - switch (Q->mode) { - case EQUIT: - lp.phi = fabs(rh) <= EPS10 ? 0. : asin(xy.y * sinz / rh); - xy.x *= sinz; - xy.y = cosz * rh; - break; - case OBLIQ: - lp.phi = fabs(rh) <= EPS10 ? P->phi0 : - asin(cosz * Q->sinb1 + xy.y * sinz * Q->cosb1 / rh); - xy.x *= sinz * Q->cosb1; - xy.y = (cosz - sin(lp.phi) * Q->sinb1) * rh; - break; - case N_POLE: - xy.y = -xy.y; - lp.phi = M_HALFPI - lp.phi; - break; - case S_POLE: - lp.phi -= M_HALFPI; - break; - } - lp.lam = (xy.y == 0. && (Q->mode == EQUIT || Q->mode == OBLIQ)) ? - 0. : atan2(xy.x, xy.y); - return (lp); -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->apa); - - return pj_default_destructor(P, errlev); -} - - -PJ *PROJECTION(laea) { - double t; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - P->destructor = destructor; - - t = fabs(P->phi0); - if (fabs(t - M_HALFPI) < EPS10) - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(t) < EPS10) - Q->mode = EQUIT; - else - Q->mode = OBLIQ; - if (P->es != 0.0) { - double sinphi; - - P->e = sqrt(P->es); - Q->qp = pj_qsfn(1., P->e, P->one_es); - Q->mmf = .5 / (1. - P->es); - Q->apa = pj_authset(P->es); - if (0==Q->apa) - return destructor(P, ENOMEM); - switch (Q->mode) { - case N_POLE: - case S_POLE: - Q->dd = 1.; - break; - case EQUIT: - Q->dd = 1. / (Q->rq = sqrt(.5 * Q->qp)); - Q->xmf = 1.; - Q->ymf = .5 * Q->qp; - break; - case OBLIQ: - Q->rq = sqrt(.5 * Q->qp); - sinphi = sin(P->phi0); - Q->sinb1 = pj_qsfn(sinphi, P->e, P->one_es) / Q->qp; - Q->cosb1 = sqrt(1. - Q->sinb1 * Q->sinb1); - Q->dd = cos(P->phi0) / (sqrt(1. - P->es * sinphi * sinphi) * - Q->rq * Q->cosb1); - Q->ymf = (Q->xmf = Q->rq) / Q->dd; - Q->xmf *= Q->dd; - break; - } - P->inv = e_inverse; - P->fwd = e_forward; - } else { - if (Q->mode == OBLIQ) { - Q->sinb1 = sin(P->phi0); - Q->cosb1 = cos(P->phi0); - } - P->inv = s_inverse; - P->fwd = s_forward; - } - - return P; -} - diff --git a/src/PJ_laea.cpp b/src/PJ_laea.cpp new file mode 100644 index 00000000..02b34858 --- /dev/null +++ b/src/PJ_laea.cpp @@ -0,0 +1,296 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(laea, "Lambert Azimuthal Equal Area") "\n\tAzi, Sph&Ell"; + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double sinb1; + double cosb1; + double xmf; + double ymf; + double mmf; + double qp; + double dd; + double rq; + double *apa; + enum Mode mode; +}; + +#define EPS10 1.e-10 + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, sinlam, sinphi, q, sinb=0.0, cosb=0.0, b=0.0; + + coslam = cos(lp.lam); + sinlam = sin(lp.lam); + sinphi = sin(lp.phi); + q = pj_qsfn(sinphi, P->e, P->one_es); + + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinb = q / Q->qp; + cosb = sqrt(1. - sinb * sinb); + } + + switch (Q->mode) { + case OBLIQ: + b = 1. + Q->sinb1 * sinb + Q->cosb1 * cosb * coslam; + break; + case EQUIT: + b = 1. + cosb * coslam; + break; + case N_POLE: + b = M_HALFPI + lp.phi; + q = Q->qp - q; + break; + case S_POLE: + b = lp.phi - M_HALFPI; + q = Q->qp + q; + break; + } + if (fabs(b) < EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + switch (Q->mode) { + case OBLIQ: + b = sqrt(2. / b); + xy.y = Q->ymf * b * (Q->cosb1 * sinb - Q->sinb1 * cosb * coslam); + goto eqcon; + break; + case EQUIT: + b = sqrt(2. / (1. + cosb * coslam)); + xy.y = b * sinb * Q->ymf; +eqcon: + xy.x = Q->xmf * b * cosb * sinlam; + break; + case N_POLE: + case S_POLE: + if (q >= 0.) { + b = sqrt(q); + xy.x = b * sinlam; + xy.y = coslam * (Q->mode == S_POLE ? b : -b); + } else + xy.x = xy.y = 0.; + break; + } + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y = 1. + cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = 1. + Q->sinb1 * sinphi + Q->cosb1 * cosphi * coslam; +oblcon: + if (xy.y <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.y = sqrt(2. / xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + xy.y *= Q->mode == EQUIT ? sinphi : + Q->cosb1 * sinphi - Q->sinb1 * cosphi * coslam; + break; + case N_POLE: + coslam = -coslam; + /*-fallthrough*/ + case S_POLE: + if (fabs(lp.phi + P->phi0) < EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.y = M_FORTPI - lp.phi * .5; + xy.y = 2. * (Q->mode == S_POLE ? cos(xy.y) : sin(xy.y)); + xy.x = xy.y * sin(lp.lam); + xy.y *= coslam; + break; + } + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cCe, sCe, q, rho, ab=0.0; + + switch (Q->mode) { + case EQUIT: + case OBLIQ: + xy.x /= Q->dd; + xy.y *= Q->dd; + rho = hypot(xy.x, xy.y); + if (rho < EPS10) { + lp.lam = 0.; + lp.phi = P->phi0; + return lp; + } + sCe = 2. * asin(.5 * rho / Q->rq); + cCe = cos(sCe); + sCe = sin(sCe); + xy.x *= sCe; + if (Q->mode == OBLIQ) { + ab = cCe * Q->sinb1 + xy.y * sCe * Q->cosb1 / rho; + xy.y = rho * Q->cosb1 * cCe - xy.y * Q->sinb1 * sCe; + } else { + ab = xy.y * sCe / rho; + xy.y = rho * cCe; + } + break; + case N_POLE: + xy.y = -xy.y; + /*-fallthrough*/ + case S_POLE: + q = (xy.x * xy.x + xy.y * xy.y); + if (q == 0.0) { + lp.lam = 0.; + lp.phi = P->phi0; + return (lp); + } + ab = 1. - q / Q->qp; + if (Q->mode == S_POLE) + ab = - ab; + break; + } + lp.lam = atan2(xy.x, xy.y); + lp.phi = pj_authlat(asin(ab), Q->apa); + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosz=0.0, rh, sinz=0.0; + + rh = hypot(xy.x, xy.y); + if ((lp.phi = rh * .5 ) > 1.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + lp.phi = 2. * asin(lp.phi); + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinz = sin(lp.phi); + cosz = cos(lp.phi); + } + switch (Q->mode) { + case EQUIT: + lp.phi = fabs(rh) <= EPS10 ? 0. : asin(xy.y * sinz / rh); + xy.x *= sinz; + xy.y = cosz * rh; + break; + case OBLIQ: + lp.phi = fabs(rh) <= EPS10 ? P->phi0 : + asin(cosz * Q->sinb1 + xy.y * sinz * Q->cosb1 / rh); + xy.x *= sinz * Q->cosb1; + xy.y = (cosz - sin(lp.phi) * Q->sinb1) * rh; + break; + case N_POLE: + xy.y = -xy.y; + lp.phi = M_HALFPI - lp.phi; + break; + case S_POLE: + lp.phi -= M_HALFPI; + break; + } + lp.lam = (xy.y == 0. && (Q->mode == EQUIT || Q->mode == OBLIQ)) ? + 0. : atan2(xy.x, xy.y); + return (lp); +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->apa); + + return pj_default_destructor(P, errlev); +} + + +PJ *PROJECTION(laea) { + double t; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + P->destructor = destructor; + + t = fabs(P->phi0); + if (fabs(t - M_HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else if (fabs(t) < EPS10) + Q->mode = EQUIT; + else + Q->mode = OBLIQ; + if (P->es != 0.0) { + double sinphi; + + P->e = sqrt(P->es); + Q->qp = pj_qsfn(1., P->e, P->one_es); + Q->mmf = .5 / (1. - P->es); + Q->apa = pj_authset(P->es); + if (0==Q->apa) + return destructor(P, ENOMEM); + switch (Q->mode) { + case N_POLE: + case S_POLE: + Q->dd = 1.; + break; + case EQUIT: + Q->dd = 1. / (Q->rq = sqrt(.5 * Q->qp)); + Q->xmf = 1.; + Q->ymf = .5 * Q->qp; + break; + case OBLIQ: + Q->rq = sqrt(.5 * Q->qp); + sinphi = sin(P->phi0); + Q->sinb1 = pj_qsfn(sinphi, P->e, P->one_es) / Q->qp; + Q->cosb1 = sqrt(1. - Q->sinb1 * Q->sinb1); + Q->dd = cos(P->phi0) / (sqrt(1. - P->es * sinphi * sinphi) * + Q->rq * Q->cosb1); + Q->ymf = (Q->xmf = Q->rq) / Q->dd; + Q->xmf *= Q->dd; + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } else { + if (Q->mode == OBLIQ) { + Q->sinb1 = sin(P->phi0); + Q->cosb1 = cos(P->phi0); + } + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} + diff --git a/src/PJ_lagrng.c b/src/PJ_lagrng.c deleted file mode 100644 index 3856fcdc..00000000 --- a/src/PJ_lagrng.c +++ /dev/null @@ -1,96 +0,0 @@ -#define PJ_LIB__ -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(lagrng, "Lagrange") "\n\tMisc Sph\n\tW="; - -#define TOL 1e-10 - -struct pj_opaque { - double a1; - double a2; - double hrw; - double hw; - double rw; - double w; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double v, c; - - if (fabs(fabs(lp.phi) - M_HALFPI) < TOL) { - xy.x = 0; - xy.y = lp.phi < 0 ? -2. : 2.; - } else { - lp.phi = sin(lp.phi); - v = Q->a1 * pow((1. + lp.phi)/(1. - lp.phi), Q->hrw); - lp.lam *= Q->rw; - c = 0.5 * (v + 1./v) + cos(lp.lam); - if (c < TOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = 2. * sin(lp.lam) / c; - xy.y = (v - 1./v) / c; - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c, x2, y2p, y2m; - - if (fabs(fabs(xy.y) - 2.) < TOL) { - lp.phi = xy.y < 0 ? -M_HALFPI : M_HALFPI; - lp.lam = 0; - } else { - x2 = xy.x * xy.x; - y2p = 2. + xy.y; - y2m = 2. - xy.y; - c = y2p * y2m - x2; - if (fabs(c) < TOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - lp.phi = 2. * atan(pow((y2p * y2p + x2) / (Q->a2 * (y2m * y2m + x2)), Q->hw)) - M_HALFPI; - lp.lam = Q->w * atan2(4. * xy.x, c); - } - return lp; -} - - -PJ *PROJECTION(lagrng) { - double phi1; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->w = pj_param(P->ctx, P->params, "dW").f; - if (Q->w <= 0) - return pj_default_destructor(P, PJD_ERR_W_OR_M_ZERO_OR_LESS); - Q->hw = 0.5 * Q->w; - Q->rw = 1. / Q->w; - Q->hrw = 0.5 * Q->rw; - phi1 = sin(pj_param(P->ctx, P->params, "rlat_1").f); - if (fabs(fabs(phi1) - 1.) < TOL) - return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90); - - Q->a1 = pow((1. - phi1)/(1. + phi1), Q->hrw); - Q->a2 = Q->a1 * Q->a1; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - diff --git a/src/PJ_lagrng.cpp b/src/PJ_lagrng.cpp new file mode 100644 index 00000000..f5363287 --- /dev/null +++ b/src/PJ_lagrng.cpp @@ -0,0 +1,96 @@ +#define PJ_LIB__ +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(lagrng, "Lagrange") "\n\tMisc Sph\n\tW="; + +#define TOL 1e-10 + +struct pj_opaque { + double a1; + double a2; + double hrw; + double hw; + double rw; + double w; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double v, c; + + if (fabs(fabs(lp.phi) - M_HALFPI) < TOL) { + xy.x = 0; + xy.y = lp.phi < 0 ? -2. : 2.; + } else { + lp.phi = sin(lp.phi); + v = Q->a1 * pow((1. + lp.phi)/(1. - lp.phi), Q->hrw); + lp.lam *= Q->rw; + c = 0.5 * (v + 1./v) + cos(lp.lam); + if (c < TOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = 2. * sin(lp.lam) / c; + xy.y = (v - 1./v) / c; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c, x2, y2p, y2m; + + if (fabs(fabs(xy.y) - 2.) < TOL) { + lp.phi = xy.y < 0 ? -M_HALFPI : M_HALFPI; + lp.lam = 0; + } else { + x2 = xy.x * xy.x; + y2p = 2. + xy.y; + y2m = 2. - xy.y; + c = y2p * y2m - x2; + if (fabs(c) < TOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + lp.phi = 2. * atan(pow((y2p * y2p + x2) / (Q->a2 * (y2m * y2m + x2)), Q->hw)) - M_HALFPI; + lp.lam = Q->w * atan2(4. * xy.x, c); + } + return lp; +} + + +PJ *PROJECTION(lagrng) { + double phi1; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->w = pj_param(P->ctx, P->params, "dW").f; + if (Q->w <= 0) + return pj_default_destructor(P, PJD_ERR_W_OR_M_ZERO_OR_LESS); + Q->hw = 0.5 * Q->w; + Q->rw = 1. / Q->w; + Q->hrw = 0.5 * Q->rw; + phi1 = sin(pj_param(P->ctx, P->params, "rlat_1").f); + if (fabs(fabs(phi1) - 1.) < TOL) + return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90); + + Q->a1 = pow((1. - phi1)/(1. + phi1), Q->hrw); + Q->a2 = Q->a1 * Q->a1; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + diff --git a/src/PJ_larr.c b/src/PJ_larr.c deleted file mode 100644 index e4d5d240..00000000 --- a/src/PJ_larr.c +++ /dev/null @@ -1,28 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(larr, "Larrivee") "\n\tMisc Sph, no inv"; - -#define SIXTH .16666666666666666 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = 0.5 * lp.lam * (1. + sqrt(cos(lp.phi))); - xy.y = lp.phi / (cos(0.5 * lp.phi) * cos(SIXTH * lp.lam)); - return xy; -} - - -PJ *PROJECTION(larr) { - - P->es = 0; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_larr.cpp b/src/PJ_larr.cpp new file mode 100644 index 00000000..e4d5d240 --- /dev/null +++ b/src/PJ_larr.cpp @@ -0,0 +1,28 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(larr, "Larrivee") "\n\tMisc Sph, no inv"; + +#define SIXTH .16666666666666666 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = 0.5 * lp.lam * (1. + sqrt(cos(lp.phi))); + xy.y = lp.phi / (cos(0.5 * lp.phi) * cos(SIXTH * lp.lam)); + return xy; +} + + +PJ *PROJECTION(larr) { + + P->es = 0; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_lask.c b/src/PJ_lask.c deleted file mode 100644 index 46f23edb..00000000 --- a/src/PJ_lask.c +++ /dev/null @@ -1,39 +0,0 @@ -#define PJ_LIB__ -#include "projects.h" - -PROJ_HEAD(lask, "Laskowski") "\n\tMisc Sph, no inv"; - -#define a10 0.975534 -#define a12 -0.119161 -#define a32 -0.0143059 -#define a14 -0.0547009 -#define b01 1.00384 -#define b21 0.0802894 -#define b03 0.0998909 -#define b41 0.000199025 -#define b23 -0.0285500 -#define b05 -0.0491032 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double l2, p2; - (void) P; - - l2 = lp.lam * lp.lam; - p2 = lp.phi * lp.phi; - xy.x = lp.lam * (a10 + p2 * (a12 + l2 * a32 + p2 * a14)); - xy.y = lp.phi * (b01 + l2 * (b21 + p2 * b23 + l2 * b41) + - p2 * (b03 + p2 * b05)); - return xy; -} - - -PJ *PROJECTION(lask) { - - P->fwd = s_forward; - P->es = 0.; - - return P; -} - diff --git a/src/PJ_lask.cpp b/src/PJ_lask.cpp new file mode 100644 index 00000000..46f23edb --- /dev/null +++ b/src/PJ_lask.cpp @@ -0,0 +1,39 @@ +#define PJ_LIB__ +#include "projects.h" + +PROJ_HEAD(lask, "Laskowski") "\n\tMisc Sph, no inv"; + +#define a10 0.975534 +#define a12 -0.119161 +#define a32 -0.0143059 +#define a14 -0.0547009 +#define b01 1.00384 +#define b21 0.0802894 +#define b03 0.0998909 +#define b41 0.000199025 +#define b23 -0.0285500 +#define b05 -0.0491032 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double l2, p2; + (void) P; + + l2 = lp.lam * lp.lam; + p2 = lp.phi * lp.phi; + xy.x = lp.lam * (a10 + p2 * (a12 + l2 * a32 + p2 * a14)); + xy.y = lp.phi * (b01 + l2 * (b21 + p2 * b23 + l2 * b41) + + p2 * (b03 + p2 * b05)); + return xy; +} + + +PJ *PROJECTION(lask) { + + P->fwd = s_forward; + P->es = 0.; + + return P; +} + diff --git a/src/PJ_latlong.c b/src/PJ_latlong.c deleted file mode 100644 index 1331d59a..00000000 --- a/src/PJ_latlong.c +++ /dev/null @@ -1,124 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Stub projection implementation for lat/long coordinates. We - * don't actually change the coordinates, but we want proj=latlong - * to act sort of like a projection. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -/* very loosely based upon DMA code by Bradford W. Drew */ -#define PJ_LIB__ -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(lonlat, "Lat/long (Geodetic)") "\n\t"; -PROJ_HEAD(latlon, "Lat/long (Geodetic alias)") "\n\t"; -PROJ_HEAD(latlong, "Lat/long (Geodetic alias)") "\n\t"; -PROJ_HEAD(longlat, "Lat/long (Geodetic alias)") "\n\t"; - - - static XY latlong_forward(LP lp, PJ *P) { - XY xy = {0.0,0.0}; - (void) P; - xy.x = lp.lam; - xy.y = lp.phi; - return xy; -} - - -static LP latlong_inverse(XY xy, PJ *P) { - LP lp = {0.0,0.0}; - (void) P; - lp.phi = xy.y; - lp.lam = xy.x; - return lp; -} - - - static XYZ latlong_forward_3d (LPZ lpz, PJ *P) { - XYZ xyz = {0,0,0}; - (void) P; - xyz.x = lpz.lam; - xyz.y = lpz.phi; - xyz.z = lpz.z; - return xyz; -} - - -static LPZ latlong_inverse_3d (XYZ xyz, PJ *P) { - LPZ lpz = {0,0,0}; - (void) P; - lpz.lam = xyz.x; - lpz.phi = xyz.y; - lpz.z = xyz.z; - return lpz; -} - -static PJ_COORD latlong_forward_4d (PJ_COORD obs, PJ *P) { - (void) P; - return obs; -} - - -static PJ_COORD latlong_inverse_4d (PJ_COORD obs, PJ *P) { - (void) P; - return obs; -} - - - -static PJ *latlong_setup (PJ *P) { - P->is_latlong = 1; - P->x0 = 0; - P->y0 = 0; - P->inv = latlong_inverse; - P->fwd = latlong_forward; - P->inv3d = latlong_inverse_3d; - P->fwd3d = latlong_forward_3d; - P->inv4d = latlong_inverse_4d; - P->fwd4d = latlong_forward_4d; - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - return P; -} - -PJ *PROJECTION(latlong) { - return latlong_setup (P); -} - - -PJ *PROJECTION(longlat) { - return latlong_setup (P); -} - - -PJ *PROJECTION(latlon) { - return latlong_setup (P); -} - - -PJ *PROJECTION(lonlat) { - return latlong_setup (P); -} - diff --git a/src/PJ_latlong.cpp b/src/PJ_latlong.cpp new file mode 100644 index 00000000..1331d59a --- /dev/null +++ b/src/PJ_latlong.cpp @@ -0,0 +1,124 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Stub projection implementation for lat/long coordinates. We + * don't actually change the coordinates, but we want proj=latlong + * to act sort of like a projection. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +/* very loosely based upon DMA code by Bradford W. Drew */ +#define PJ_LIB__ +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(lonlat, "Lat/long (Geodetic)") "\n\t"; +PROJ_HEAD(latlon, "Lat/long (Geodetic alias)") "\n\t"; +PROJ_HEAD(latlong, "Lat/long (Geodetic alias)") "\n\t"; +PROJ_HEAD(longlat, "Lat/long (Geodetic alias)") "\n\t"; + + + static XY latlong_forward(LP lp, PJ *P) { + XY xy = {0.0,0.0}; + (void) P; + xy.x = lp.lam; + xy.y = lp.phi; + return xy; +} + + +static LP latlong_inverse(XY xy, PJ *P) { + LP lp = {0.0,0.0}; + (void) P; + lp.phi = xy.y; + lp.lam = xy.x; + return lp; +} + + + static XYZ latlong_forward_3d (LPZ lpz, PJ *P) { + XYZ xyz = {0,0,0}; + (void) P; + xyz.x = lpz.lam; + xyz.y = lpz.phi; + xyz.z = lpz.z; + return xyz; +} + + +static LPZ latlong_inverse_3d (XYZ xyz, PJ *P) { + LPZ lpz = {0,0,0}; + (void) P; + lpz.lam = xyz.x; + lpz.phi = xyz.y; + lpz.z = xyz.z; + return lpz; +} + +static PJ_COORD latlong_forward_4d (PJ_COORD obs, PJ *P) { + (void) P; + return obs; +} + + +static PJ_COORD latlong_inverse_4d (PJ_COORD obs, PJ *P) { + (void) P; + return obs; +} + + + +static PJ *latlong_setup (PJ *P) { + P->is_latlong = 1; + P->x0 = 0; + P->y0 = 0; + P->inv = latlong_inverse; + P->fwd = latlong_forward; + P->inv3d = latlong_inverse_3d; + P->fwd3d = latlong_forward_3d; + P->inv4d = latlong_inverse_4d; + P->fwd4d = latlong_forward_4d; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + return P; +} + +PJ *PROJECTION(latlong) { + return latlong_setup (P); +} + + +PJ *PROJECTION(longlat) { + return latlong_setup (P); +} + + +PJ *PROJECTION(latlon) { + return latlong_setup (P); +} + + +PJ *PROJECTION(lonlat) { + return latlong_setup (P); +} + diff --git a/src/PJ_lcc.c b/src/PJ_lcc.c deleted file mode 100644 index 96aa3f2a..00000000 --- a/src/PJ_lcc.c +++ /dev/null @@ -1,128 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(lcc, "Lambert Conformal Conic") - "\n\tConic, Sph&Ell\n\tlat_1= and lat_2= or lat_0, k_0="; - -#define EPS10 1.e-10 - -struct pj_opaque { - double phi1; - double phi2; - double n; - double rho0; - double c; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0., 0.}; - struct pj_opaque *Q = P->opaque; - double rho; - - if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) { - if ((lp.phi * Q->n) <= 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - rho = 0.; - } else { - rho = Q->c * (P->es != 0. ? - pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->n) : - pow(tan(M_FORTPI + .5 * lp.phi), -Q->n)); - } - lp.lam *= Q->n; - xy.x = P->k0 * (rho * sin(lp.lam)); - xy.y = P->k0 * (Q->rho0 - rho * cos(lp.lam)); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0., 0.}; - struct pj_opaque *Q = P->opaque; - double rho; - - xy.x /= P->k0; - xy.y /= P->k0; - - xy.y = Q->rho0 - xy.y; - rho = hypot(xy.x, xy.y); - if (rho != 0.) { - if (Q->n < 0.) { - rho = -rho; - xy.x = -xy.x; - xy.y = -xy.y; - } - if (P->es != 0.) { - lp.phi = pj_phi2(P->ctx, pow(rho / Q->c, 1./Q->n), P->e); - if (lp.phi == HUGE_VAL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - - } else - lp.phi = 2. * atan(pow(Q->c / rho, 1./Q->n)) - M_HALFPI; - lp.lam = atan2(xy.x, xy.y) / Q->n; - } else { - lp.lam = 0.; - lp.phi = Q->n > 0. ? M_HALFPI : -M_HALFPI; - } - return lp; -} - - -PJ *PROJECTION(lcc) { - double cosphi, sinphi; - int secant; - struct pj_opaque *Q = pj_calloc(1, sizeof (struct pj_opaque)); - - if (0 == Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (pj_param(P->ctx, P->params, "tlat_2").i) - Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - else { - Q->phi2 = Q->phi1; - if (!pj_param(P->ctx, P->params, "tlat_0").i) - P->phi0 = Q->phi1; - } - if (fabs(Q->phi1 + Q->phi2) < EPS10) - return pj_default_destructor(P, PJD_ERR_CONIC_LAT_EQUAL); - - Q->n = sinphi = sin(Q->phi1); - cosphi = cos(Q->phi1); - secant = fabs(Q->phi1 - Q->phi2) >= EPS10; - if (P->es != 0.) { - double ml1, m1; - - m1 = pj_msfn(sinphi, cosphi, P->es); - ml1 = pj_tsfn(Q->phi1, sinphi, P->e); - if (secant) { /* secant cone */ - sinphi = sin(Q->phi2); - Q->n = log(m1 / pj_msfn(sinphi, cos(Q->phi2), P->es)); - Q->n /= log(ml1 / pj_tsfn(Q->phi2, sinphi, P->e)); - } - Q->c = (Q->rho0 = m1 * pow(ml1, -Q->n) / Q->n); - Q->rho0 *= (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) ? 0. : - pow(pj_tsfn(P->phi0, sin(P->phi0), P->e), Q->n); - } else { - if (secant) - Q->n = log(cosphi / cos(Q->phi2)) / - log(tan(M_FORTPI + .5 * Q->phi2) / - tan(M_FORTPI + .5 * Q->phi1)); - Q->c = cosphi * pow(tan(M_FORTPI + .5 * Q->phi1), Q->n) / Q->n; - Q->rho0 = (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) ? 0. : - Q->c * pow(tan(M_FORTPI + .5 * P->phi0), -Q->n); - } - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_lcc.cpp b/src/PJ_lcc.cpp new file mode 100644 index 00000000..5c430ea0 --- /dev/null +++ b/src/PJ_lcc.cpp @@ -0,0 +1,128 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(lcc, "Lambert Conformal Conic") + "\n\tConic, Sph&Ell\n\tlat_1= and lat_2= or lat_0, k_0="; + +#define EPS10 1.e-10 + +struct pj_opaque { + double phi1; + double phi2; + double n; + double rho0; + double c; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0., 0.}; + struct pj_opaque *Q = static_cast(P->opaque); + double rho; + + if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) { + if ((lp.phi * Q->n) <= 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + rho = 0.; + } else { + rho = Q->c * (P->es != 0. ? + pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->n) : + pow(tan(M_FORTPI + .5 * lp.phi), -Q->n)); + } + lp.lam *= Q->n; + xy.x = P->k0 * (rho * sin(lp.lam)); + xy.y = P->k0 * (Q->rho0 - rho * cos(lp.lam)); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0., 0.}; + struct pj_opaque *Q = static_cast(P->opaque); + double rho; + + xy.x /= P->k0; + xy.y /= P->k0; + + xy.y = Q->rho0 - xy.y; + rho = hypot(xy.x, xy.y); + if (rho != 0.) { + if (Q->n < 0.) { + rho = -rho; + xy.x = -xy.x; + xy.y = -xy.y; + } + if (P->es != 0.) { + lp.phi = pj_phi2(P->ctx, pow(rho / Q->c, 1./Q->n), P->e); + if (lp.phi == HUGE_VAL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + + } else + lp.phi = 2. * atan(pow(Q->c / rho, 1./Q->n)) - M_HALFPI; + lp.lam = atan2(xy.x, xy.y) / Q->n; + } else { + lp.lam = 0.; + lp.phi = Q->n > 0. ? M_HALFPI : -M_HALFPI; + } + return lp; +} + + +PJ *PROJECTION(lcc) { + double cosphi, sinphi; + int secant; + struct pj_opaque *Q = static_cast(pj_calloc(1, sizeof (struct pj_opaque))); + + if (0 == Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (pj_param(P->ctx, P->params, "tlat_2").i) + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + else { + Q->phi2 = Q->phi1; + if (!pj_param(P->ctx, P->params, "tlat_0").i) + P->phi0 = Q->phi1; + } + if (fabs(Q->phi1 + Q->phi2) < EPS10) + return pj_default_destructor(P, PJD_ERR_CONIC_LAT_EQUAL); + + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if (P->es != 0.) { + double ml1, m1; + + m1 = pj_msfn(sinphi, cosphi, P->es); + ml1 = pj_tsfn(Q->phi1, sinphi, P->e); + if (secant) { /* secant cone */ + sinphi = sin(Q->phi2); + Q->n = log(m1 / pj_msfn(sinphi, cos(Q->phi2), P->es)); + Q->n /= log(ml1 / pj_tsfn(Q->phi2, sinphi, P->e)); + } + Q->c = (Q->rho0 = m1 * pow(ml1, -Q->n) / Q->n); + Q->rho0 *= (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) ? 0. : + pow(pj_tsfn(P->phi0, sin(P->phi0), P->e), Q->n); + } else { + if (secant) + Q->n = log(cosphi / cos(Q->phi2)) / + log(tan(M_FORTPI + .5 * Q->phi2) / + tan(M_FORTPI + .5 * Q->phi1)); + Q->c = cosphi * pow(tan(M_FORTPI + .5 * Q->phi1), Q->n) / Q->n; + Q->rho0 = (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) ? 0. : + Q->c * pow(tan(M_FORTPI + .5 * P->phi0), -Q->n); + } + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_lcca.c b/src/PJ_lcca.c deleted file mode 100644 index f0f256b1..00000000 --- a/src/PJ_lcca.c +++ /dev/null @@ -1,162 +0,0 @@ -/***************************************************************************** - - Lambert Conformal Conic Alternative - ----------------------------------- - - This is Gerald Evenden's 2003 implementation of an alternative - "almost" LCC, which has been in use historically, but which - should NOT be used for new projects - i.e: use this implementation - if you need interoperability with old data represented in this - projection, but not in any other case. - - The code was originally discussed on the PROJ.4 mailing list in - a thread archived over at - - http://lists.maptools.org/pipermail/proj/2003-March/000644.html - - It was discussed again in the thread starting at - - http://lists.maptools.org/pipermail/proj/2017-October/007828.html - and continuing at - http://lists.maptools.org/pipermail/proj/2017-November/007831.html - - which prompted Clifford J. Mugnier to add these clarifying notes: - - The French Army Truncated Cubic Lambert (partially conformal) Conic - projection is the Legal system for the projection in France between - the late 1800s and 1948 when the French Legislature changed the law - to recognize the fully conformal version. - - It was (might still be in one or two North African prior French - Colonies) used in North Africa in Algeria, Tunisia, & Morocco, as - well as in Syria during the Levant. - - Last time I have seen it used was about 30+ years ago in - Algeria when it was used to define Lease Block boundaries for - Petroleum Exploration & Production. - - (signed) - - Clifford J. Mugnier, c.p., c.m.s. - Chief of Geodesy - LSU Center for GeoInformatics - Dept. of Civil Engineering - LOUISIANA STATE UNIVERSITY - -*****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(lcca, "Lambert Conformal Conic Alternative") - "\n\tConic, Sph&Ell\n\tlat_0="; - -#define MAX_ITER 10 -#define DEL_TOL 1e-12 - -struct pj_opaque { - double *en; - double r0, l, M0; - double C; -}; - - -static double fS(double S, double C) { /* func to compute dr */ - - return S * ( 1. + S * S * C); -} - - -static double fSp(double S, double C) { /* deriv of fs */ - - return 1. + 3.* S * S * C; -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double S, r, dr; - - S = pj_mlfn(lp.phi, sin(lp.phi), cos(lp.phi), Q->en) - Q->M0; - dr = fS(S, Q->C); - r = Q->r0 - dr; - xy.x = P->k0 * (r * sin( lp.lam *= Q->l ) ); - xy.y = P->k0 * (Q->r0 - r * cos(lp.lam) ); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double theta, dr, S, dif; - int i; - - xy.x /= P->k0; - xy.y /= P->k0; - theta = atan2(xy.x , Q->r0 - xy.y); - dr = xy.y - xy.x * tan(0.5 * theta); - lp.lam = theta / Q->l; - S = dr; - for (i = MAX_ITER; i ; --i) { - S -= (dif = (fS(S, Q->C) - dr) / fSp(S, Q->C)); - if (fabs(dif) < DEL_TOL) break; - } - if (!i) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - lp.phi = pj_inv_mlfn(P->ctx, S + Q->M0, P->es, Q->en); - - return lp; -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(lcca) { - double s2p0, N0, R0, tan0; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - (Q->en = pj_enfn(P->es)); - if (!Q->en) - return pj_default_destructor (P, ENOMEM); - - if (P->phi0 == 0.) { - return destructor(P, PJD_ERR_LAT_0_IS_ZERO); - } - Q->l = sin(P->phi0); - Q->M0 = pj_mlfn(P->phi0, Q->l, cos(P->phi0), Q->en); - s2p0 = Q->l * Q->l; - R0 = 1. / (1. - P->es * s2p0); - N0 = sqrt(R0); - R0 *= P->one_es * N0; - tan0 = tan(P->phi0); - Q->r0 = N0 / tan0; - Q->C = 1. / (6. * R0 * N0); - - P->inv = e_inverse; - P->fwd = e_forward; - P->destructor = destructor; - - return P; -} diff --git a/src/PJ_lcca.cpp b/src/PJ_lcca.cpp new file mode 100644 index 00000000..cbb18709 --- /dev/null +++ b/src/PJ_lcca.cpp @@ -0,0 +1,162 @@ +/***************************************************************************** + + Lambert Conformal Conic Alternative + ----------------------------------- + + This is Gerald Evenden's 2003 implementation of an alternative + "almost" LCC, which has been in use historically, but which + should NOT be used for new projects - i.e: use this implementation + if you need interoperability with old data represented in this + projection, but not in any other case. + + The code was originally discussed on the PROJ.4 mailing list in + a thread archived over at + + http://lists.maptools.org/pipermail/proj/2003-March/000644.html + + It was discussed again in the thread starting at + + http://lists.maptools.org/pipermail/proj/2017-October/007828.html + and continuing at + http://lists.maptools.org/pipermail/proj/2017-November/007831.html + + which prompted Clifford J. Mugnier to add these clarifying notes: + + The French Army Truncated Cubic Lambert (partially conformal) Conic + projection is the Legal system for the projection in France between + the late 1800s and 1948 when the French Legislature changed the law + to recognize the fully conformal version. + + It was (might still be in one or two North African prior French + Colonies) used in North Africa in Algeria, Tunisia, & Morocco, as + well as in Syria during the Levant. + + Last time I have seen it used was about 30+ years ago in + Algeria when it was used to define Lease Block boundaries for + Petroleum Exploration & Production. + + (signed) + + Clifford J. Mugnier, c.p., c.m.s. + Chief of Geodesy + LSU Center for GeoInformatics + Dept. of Civil Engineering + LOUISIANA STATE UNIVERSITY + +*****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(lcca, "Lambert Conformal Conic Alternative") + "\n\tConic, Sph&Ell\n\tlat_0="; + +#define MAX_ITER 10 +#define DEL_TOL 1e-12 + +struct pj_opaque { + double *en; + double r0, l, M0; + double C; +}; + + +static double fS(double S, double C) { /* func to compute dr */ + + return S * ( 1. + S * S * C); +} + + +static double fSp(double S, double C) { /* deriv of fs */ + + return 1. + 3.* S * S * C; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double S, r, dr; + + S = pj_mlfn(lp.phi, sin(lp.phi), cos(lp.phi), Q->en) - Q->M0; + dr = fS(S, Q->C); + r = Q->r0 - dr; + xy.x = P->k0 * (r * sin( lp.lam *= Q->l ) ); + xy.y = P->k0 * (Q->r0 - r * cos(lp.lam) ); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double theta, dr, S, dif; + int i; + + xy.x /= P->k0; + xy.y /= P->k0; + theta = atan2(xy.x , Q->r0 - xy.y); + dr = xy.y - xy.x * tan(0.5 * theta); + lp.lam = theta / Q->l; + S = dr; + for (i = MAX_ITER; i ; --i) { + S -= (dif = (fS(S, Q->C) - dr) / fSp(S, Q->C)); + if (fabs(dif) < DEL_TOL) break; + } + if (!i) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + lp.phi = pj_inv_mlfn(P->ctx, S + Q->M0, P->es, Q->en); + + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(lcca) { + double s2p0, N0, R0, tan0; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + (Q->en = pj_enfn(P->es)); + if (!Q->en) + return pj_default_destructor (P, ENOMEM); + + if (P->phi0 == 0.) { + return destructor(P, PJD_ERR_LAT_0_IS_ZERO); + } + Q->l = sin(P->phi0); + Q->M0 = pj_mlfn(P->phi0, Q->l, cos(P->phi0), Q->en); + s2p0 = Q->l * Q->l; + R0 = 1. / (1. - P->es * s2p0); + N0 = sqrt(R0); + R0 *= P->one_es * N0; + tan0 = tan(P->phi0); + Q->r0 = N0 / tan0; + Q->C = 1. / (6. * R0 * N0); + + P->inv = e_inverse; + P->fwd = e_forward; + P->destructor = destructor; + + return P; +} diff --git a/src/PJ_loxim.c b/src/PJ_loxim.c deleted file mode 100644 index 512bfc23..00000000 --- a/src/PJ_loxim.c +++ /dev/null @@ -1,75 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(loxim, "Loximuthal") "\n\tPCyl Sph"; - -#define EPS 1e-8 - -struct pj_opaque { - double phi1; - double cosphi1; - double tanphi1; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y = lp.phi - Q->phi1; - if (fabs(xy.y) < EPS) - xy.x = lp.lam * Q->cosphi1; - else { - xy.x = M_FORTPI + 0.5 * lp.phi; - if (fabs(xy.x) < EPS || fabs(fabs(xy.x) - M_HALFPI) < EPS) - xy.x = 0.; - else - xy.x = lp.lam * xy.y / log( tan(xy.x) / Q->tanphi1 ); - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - lp.phi = xy.y + Q->phi1; - if (fabs(xy.y) < EPS) { - lp.lam = xy.x / Q->cosphi1; - } else { - lp.lam = M_FORTPI + 0.5 * lp.phi; - if (fabs(lp.lam) < EPS || fabs(fabs(lp.lam) - M_HALFPI) < EPS) - lp.lam = 0.; - else - lp.lam = xy.x * log( tan(lp.lam) / Q->tanphi1 ) / xy.y ; - } - return lp; -} - - -PJ *PROJECTION(loxim) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - Q->cosphi1 = cos(Q->phi1); - if (Q->cosphi1 < EPS) - return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90); - - - Q->tanphi1 = tan(M_FORTPI + 0.5 * Q->phi1); - - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} diff --git a/src/PJ_loxim.cpp b/src/PJ_loxim.cpp new file mode 100644 index 00000000..28e955d9 --- /dev/null +++ b/src/PJ_loxim.cpp @@ -0,0 +1,75 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(loxim, "Loximuthal") "\n\tPCyl Sph"; + +#define EPS 1e-8 + +struct pj_opaque { + double phi1; + double cosphi1; + double tanphi1; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y = lp.phi - Q->phi1; + if (fabs(xy.y) < EPS) + xy.x = lp.lam * Q->cosphi1; + else { + xy.x = M_FORTPI + 0.5 * lp.phi; + if (fabs(xy.x) < EPS || fabs(fabs(xy.x) - M_HALFPI) < EPS) + xy.x = 0.; + else + xy.x = lp.lam * xy.y / log( tan(xy.x) / Q->tanphi1 ); + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + lp.phi = xy.y + Q->phi1; + if (fabs(xy.y) < EPS) { + lp.lam = xy.x / Q->cosphi1; + } else { + lp.lam = M_FORTPI + 0.5 * lp.phi; + if (fabs(lp.lam) < EPS || fabs(fabs(lp.lam) - M_HALFPI) < EPS) + lp.lam = 0.; + else + lp.lam = xy.x * log( tan(lp.lam) / Q->tanphi1 ) / xy.y ; + } + return lp; +} + + +PJ *PROJECTION(loxim) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->cosphi1 = cos(Q->phi1); + if (Q->cosphi1 < EPS) + return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90); + + + Q->tanphi1 = tan(M_FORTPI + 0.5 * Q->phi1); + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} diff --git a/src/PJ_lsat.c b/src/PJ_lsat.c deleted file mode 100644 index 810a1cba..00000000 --- a/src/PJ_lsat.c +++ /dev/null @@ -1,210 +0,0 @@ -/* based upon Snyder and Linck, USGS-NMD */ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(lsat, "Space oblique for LANDSAT") - "\n\tCyl, Sph&Ell\n\tlsat= path="; - -#define TOL 1e-7 - -struct pj_opaque { - double a2, a4, b, c1, c3; - double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; -}; - -static void seraz0(double lam, double mult, PJ *P) { - struct pj_opaque *Q = P->opaque; - double sdsq, h, s, fc, sd, sq, d__1 = 0; - - lam *= DEG_TO_RAD; - sd = sin(lam); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) - / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - - d__1 = 1. + Q->q * sdsq; - h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + Q->w * sdsq) - / (d__1 * d__1) - Q->p22 * Q->ca); - - sq = sqrt(Q->xj * Q->xj + s * s); - fc = mult * (h * Q->xj - s * s) / sq; - Q->b += fc; - Q->a2 += fc * cos(lam + lam); - Q->a4 += fc * cos(lam * 4.); - fc = mult * s * (h + Q->xj) / sq; - Q->c1 += fc * cos(lam); - Q->c3 += fc * cos(lam * 3.); -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int l, nn; - double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph; - double lamtp, cl, sd, sp, sav, tanphi; - - if (lp.phi > M_HALFPI) - lp.phi = M_HALFPI; - else if (lp.phi < -M_HALFPI) - lp.phi = -M_HALFPI; - - if (lp.phi >= 0. ) - lampp = M_HALFPI; - else - lampp = M_PI_HALFPI; - tanphi = tan(lp.phi); - for (nn = 0;;) { - double fac; - sav = lampp; - lamtp = lp.lam + Q->p22 * lampp; - cl = cos(lamtp); - if( cl < 0 ) - fac = lampp + sin(lampp) * M_HALFPI; - else - fac = lampp - sin(lampp) * M_HALFPI; - for (l = 50; l >= 0; --l) { - lamt = lp.lam + Q->p22 * sav; - c = cos(lamt); - if (fabs(c) < TOL) - lamt -= TOL; - xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; - lamdp = atan(xlam) + fac; - if (fabs(fabs(sav) - fabs(lamdp)) < TOL) - break; - sav = lamdp; - } - if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) - break; - if (lamdp <= Q->rlm) - lampp = M_TWOPI_HALFPI; - else if (lamdp >= Q->rlm2) - lampp = M_HALFPI; - } - if (l) { - sp = sin(lp.phi); - phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * - sin(lamt)) / sqrt(1. - P->es * sp * sp)); - tanph = log(tan(M_FORTPI + .5 * phidp)); - sd = sin(lamdp); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) - / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - d = sqrt(Q->xj * Q->xj + s * s); - xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * - sin(lamdp * 4.) - tanph * s / d; - xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; - } else - xy.x = xy.y = HUGE_VAL; - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int nn; - double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; - - lamdp = xy.x / Q->b; - nn = 50; - do { - sav = lamdp; - sd = sin(lamdp); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) - / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( - 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( - Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); - lamdp /= Q->b; - } while (fabs(lamdp - sav) >= TOL && --nn); - sl = sin(lamdp); - fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - - Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); - phidp = 2. * (atan(fac) - M_FORTPI); - dd = sl * sl; - if (fabs(cos(lamdp)) < TOL) - lamdp -= TOL; - spp = sin(phidp); - sppsq = spp * spp; - lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * - Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( - 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq - * (1. + Q->u))); - sl = lamt >= 0. ? 1. : -1.; - scl = cos(lamdp) >= 0. ? 1. : -1; - lamt -= M_HALFPI * (1. - scl) * sl; - lp.lam = lamt - Q->p22 * lamdp; - if (fabs(Q->sa) < TOL) - lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); - else - lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / - (P->one_es * Q->sa)); - return lp; -} - - -PJ *PROJECTION(lsat) { - int land, path; - double lam, alf, esc, ess; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - land = pj_param(P->ctx, P->params, "ilsat").i; - if (land <= 0 || land > 5) - return pj_default_destructor(P, PJD_ERR_LSAT_NOT_IN_RANGE); - - path = pj_param(P->ctx, P->params, "ipath").i; - if (path <= 0 || path > (land <= 3 ? 251 : 233)) - return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE); - - if (land <= 3) { - P->lam0 = DEG_TO_RAD * 128.87 - M_TWOPI / 251. * path; - Q->p22 = 103.2669323; - alf = DEG_TO_RAD * 99.092; - } else { - P->lam0 = DEG_TO_RAD * 129.3 - M_TWOPI / 233. * path; - Q->p22 = 98.8841202; - alf = DEG_TO_RAD * 98.2; - } - Q->p22 /= 1440.; - Q->sa = sin(alf); - Q->ca = cos(alf); - if (fabs(Q->ca) < 1e-9) - Q->ca = 1e-9; - esc = P->es * Q->ca * Q->ca; - ess = P->es * Q->sa * Q->sa; - Q->w = (1. - esc) * P->rone_es; - Q->w = Q->w * Q->w - 1.; - Q->q = ess * P->rone_es; - Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; - Q->u = esc * P->rone_es; - Q->xj = P->one_es * P->one_es * P->one_es; - Q->rlm = M_PI * (1. / 248. + .5161290322580645); - Q->rlm2 = Q->rlm + M_TWOPI; - Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; - seraz0(0., 1., P); - for (lam = 9.; lam <= 81.0001; lam += 18.) - seraz0(lam, 4., P); - for (lam = 18; lam <= 72.0001; lam += 18.) - seraz0(lam, 2., P); - seraz0(90., 1., P); - Q->a2 /= 30.; - Q->a4 /= 60.; - Q->b /= 30.; - Q->c1 /= 15.; - Q->c3 /= 45.; - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_lsat.cpp b/src/PJ_lsat.cpp new file mode 100644 index 00000000..e3e7e026 --- /dev/null +++ b/src/PJ_lsat.cpp @@ -0,0 +1,210 @@ +/* based upon Snyder and Linck, USGS-NMD */ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(lsat, "Space oblique for LANDSAT") + "\n\tCyl, Sph&Ell\n\tlsat= path="; + +#define TOL 1e-7 + +struct pj_opaque { + double a2, a4, b, c1, c3; + double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; +}; + +static void seraz0(double lam, double mult, PJ *P) { + struct pj_opaque *Q = static_cast(P->opaque); + double sdsq, h, s, fc, sd, sq, d__1 = 0; + + lam *= DEG_TO_RAD; + sd = sin(lam); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + + d__1 = 1. + Q->q * sdsq; + h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + Q->w * sdsq) + / (d__1 * d__1) - Q->p22 * Q->ca); + + sq = sqrt(Q->xj * Q->xj + s * s); + fc = mult * (h * Q->xj - s * s) / sq; + Q->b += fc; + Q->a2 += fc * cos(lam + lam); + Q->a4 += fc * cos(lam * 4.); + fc = mult * s * (h + Q->xj) / sq; + Q->c1 += fc * cos(lam); + Q->c3 += fc * cos(lam * 3.); +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int l, nn; + double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph; + double lamtp, cl, sd, sp, sav, tanphi; + + if (lp.phi > M_HALFPI) + lp.phi = M_HALFPI; + else if (lp.phi < -M_HALFPI) + lp.phi = -M_HALFPI; + + if (lp.phi >= 0. ) + lampp = M_HALFPI; + else + lampp = M_PI_HALFPI; + tanphi = tan(lp.phi); + for (nn = 0;;) { + double fac; + sav = lampp; + lamtp = lp.lam + Q->p22 * lampp; + cl = cos(lamtp); + if( cl < 0 ) + fac = lampp + sin(lampp) * M_HALFPI; + else + fac = lampp - sin(lampp) * M_HALFPI; + for (l = 50; l >= 0; --l) { + lamt = lp.lam + Q->p22 * sav; + c = cos(lamt); + if (fabs(c) < TOL) + lamt -= TOL; + xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; + lamdp = atan(xlam) + fac; + if (fabs(fabs(sav) - fabs(lamdp)) < TOL) + break; + sav = lamdp; + } + if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) + break; + if (lamdp <= Q->rlm) + lampp = M_TWOPI_HALFPI; + else if (lamdp >= Q->rlm2) + lampp = M_HALFPI; + } + if (l) { + sp = sin(lp.phi); + phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * + sin(lamt)) / sqrt(1. - P->es * sp * sp)); + tanph = log(tan(M_FORTPI + .5 * phidp)); + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d = sqrt(Q->xj * Q->xj + s * s); + xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * + sin(lamdp * 4.) - tanph * s / d; + xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; + } else + xy.x = xy.y = HUGE_VAL; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int nn; + double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; + + lamdp = xy.x / Q->b; + nn = 50; + do { + sav = lamdp; + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( + 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( + Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); + lamdp /= Q->b; + } while (fabs(lamdp - sav) >= TOL && --nn); + sl = sin(lamdp); + fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - + Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); + phidp = 2. * (atan(fac) - M_FORTPI); + dd = sl * sl; + if (fabs(cos(lamdp)) < TOL) + lamdp -= TOL; + spp = sin(phidp); + sppsq = spp * spp; + lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * + Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( + 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq + * (1. + Q->u))); + sl = lamt >= 0. ? 1. : -1.; + scl = cos(lamdp) >= 0. ? 1. : -1; + lamt -= M_HALFPI * (1. - scl) * sl; + lp.lam = lamt - Q->p22 * lamdp; + if (fabs(Q->sa) < TOL) + lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); + else + lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / + (P->one_es * Q->sa)); + return lp; +} + + +PJ *PROJECTION(lsat) { + int land, path; + double lam, alf, esc, ess; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + land = pj_param(P->ctx, P->params, "ilsat").i; + if (land <= 0 || land > 5) + return pj_default_destructor(P, PJD_ERR_LSAT_NOT_IN_RANGE); + + path = pj_param(P->ctx, P->params, "ipath").i; + if (path <= 0 || path > (land <= 3 ? 251 : 233)) + return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE); + + if (land <= 3) { + P->lam0 = DEG_TO_RAD * 128.87 - M_TWOPI / 251. * path; + Q->p22 = 103.2669323; + alf = DEG_TO_RAD * 99.092; + } else { + P->lam0 = DEG_TO_RAD * 129.3 - M_TWOPI / 233. * path; + Q->p22 = 98.8841202; + alf = DEG_TO_RAD * 98.2; + } + Q->p22 /= 1440.; + Q->sa = sin(alf); + Q->ca = cos(alf); + if (fabs(Q->ca) < 1e-9) + Q->ca = 1e-9; + esc = P->es * Q->ca * Q->ca; + ess = P->es * Q->sa * Q->sa; + Q->w = (1. - esc) * P->rone_es; + Q->w = Q->w * Q->w - 1.; + Q->q = ess * P->rone_es; + Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; + Q->u = esc * P->rone_es; + Q->xj = P->one_es * P->one_es * P->one_es; + Q->rlm = M_PI * (1. / 248. + .5161290322580645); + Q->rlm2 = Q->rlm + M_TWOPI; + Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; + seraz0(0., 1., P); + for (lam = 9.; lam <= 81.0001; lam += 18.) + seraz0(lam, 4., P); + for (lam = 18; lam <= 72.0001; lam += 18.) + seraz0(lam, 2., P); + seraz0(90., 1., P); + Q->a2 /= 30.; + Q->a4 /= 60.; + Q->b /= 30.; + Q->c1 /= 15.; + Q->c3 /= 45.; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_mbt_fps.c b/src/PJ_mbt_fps.c deleted file mode 100644 index 66ed9458..00000000 --- a/src/PJ_mbt_fps.c +++ /dev/null @@ -1,57 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(mbt_fps, "McBryde-Thomas Flat-Pole Sine (No. 2)") "\n\tCyl, Sph"; - -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 -#define C1 0.45503 -#define C2 1.36509 -#define C3 1.41546 -#define C_x 0.22248 -#define C_y 1.44492 -#define C1_2 0.33333333333333333333333333 - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double k, V, t; - int i; - (void) P; - - k = C3 * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - t = lp.phi / C2; - lp.phi -= V = (C1 * sin(t) + sin(lp.phi) - k) / - (C1_2 * cos(t) + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - t = lp.phi / C2; - xy.x = C_x * lp.lam * (1. + 3. * cos(lp.phi)/cos(t) ); - xy.y = C_y * sin(t); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double t; - - lp.phi = C2 * (t = aasin(P->ctx,xy.y / C_y)); - lp.lam = xy.x / (C_x * (1. + 3. * cos(lp.phi)/cos(t))); - lp.phi = aasin(P->ctx,(C1 * sin(t) + sin(lp.phi)) / C3); - return (lp); -} - - -PJ *PROJECTION(mbt_fps) { - - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_mbt_fps.cpp b/src/PJ_mbt_fps.cpp new file mode 100644 index 00000000..66ed9458 --- /dev/null +++ b/src/PJ_mbt_fps.cpp @@ -0,0 +1,57 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(mbt_fps, "McBryde-Thomas Flat-Pole Sine (No. 2)") "\n\tCyl, Sph"; + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 +#define C1 0.45503 +#define C2 1.36509 +#define C3 1.41546 +#define C_x 0.22248 +#define C_y 1.44492 +#define C1_2 0.33333333333333333333333333 + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double k, V, t; + int i; + (void) P; + + k = C3 * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + t = lp.phi / C2; + lp.phi -= V = (C1 * sin(t) + sin(lp.phi) - k) / + (C1_2 * cos(t) + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + t = lp.phi / C2; + xy.x = C_x * lp.lam * (1. + 3. * cos(lp.phi)/cos(t) ); + xy.y = C_y * sin(t); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + lp.phi = C2 * (t = aasin(P->ctx,xy.y / C_y)); + lp.lam = xy.x / (C_x * (1. + 3. * cos(lp.phi)/cos(t))); + lp.phi = aasin(P->ctx,(C1 * sin(t) + sin(lp.phi)) / C3); + return (lp); +} + + +PJ *PROJECTION(mbt_fps) { + + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_mbtfpp.c b/src/PJ_mbtfpp.c deleted file mode 100644 index 276a43eb..00000000 --- a/src/PJ_mbtfpp.c +++ /dev/null @@ -1,65 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(mbtfpp, "McBride-Thomas Flat-Polar Parabolic") "\n\tCyl, Sph"; - -#define CS .95257934441568037152 -#define FXC .92582009977255146156 -#define FYC 3.40168025708304504493 -#define C23 .66666666666666666666 -#define C13 .33333333333333333333 -#define ONEEPS 1.0000001 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - lp.phi = asin(CS * sin(lp.phi)); - xy.x = FXC * lp.lam * (2. * cos(C23 * lp.phi) - 1.); - xy.y = FYC * sin(C13 * lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - - lp.phi = xy.y / FYC; - if (fabs(lp.phi) >= 1.) { - if (fabs(lp.phi) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; - } - } else - lp.phi = asin(lp.phi); - - lp.lam = xy.x / ( FXC * (2. * cos(C23 * (lp.phi *= 3.)) - 1.) ); - if (fabs(lp.phi = sin(lp.phi) / CS) >= 1.) { - if (fabs(lp.phi) > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } else { - lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; - } - } else - lp.phi = asin(lp.phi); - - return lp; -} - - -PJ *PROJECTION(mbtfpp) { - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_mbtfpp.cpp b/src/PJ_mbtfpp.cpp new file mode 100644 index 00000000..276a43eb --- /dev/null +++ b/src/PJ_mbtfpp.cpp @@ -0,0 +1,65 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(mbtfpp, "McBride-Thomas Flat-Polar Parabolic") "\n\tCyl, Sph"; + +#define CS .95257934441568037152 +#define FXC .92582009977255146156 +#define FYC 3.40168025708304504493 +#define C23 .66666666666666666666 +#define C13 .33333333333333333333 +#define ONEEPS 1.0000001 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + lp.phi = asin(CS * sin(lp.phi)); + xy.x = FXC * lp.lam * (2. * cos(C23 * lp.phi) - 1.); + xy.y = FYC * sin(C13 * lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + + lp.phi = xy.y / FYC; + if (fabs(lp.phi) >= 1.) { + if (fabs(lp.phi) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; + } + } else + lp.phi = asin(lp.phi); + + lp.lam = xy.x / ( FXC * (2. * cos(C23 * (lp.phi *= 3.)) - 1.) ); + if (fabs(lp.phi = sin(lp.phi) / CS) >= 1.) { + if (fabs(lp.phi) > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } else { + lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; + } + } else + lp.phi = asin(lp.phi); + + return lp; +} + + +PJ *PROJECTION(mbtfpp) { + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_mbtfpq.c b/src/PJ_mbtfpq.c deleted file mode 100644 index b7c0eb16..00000000 --- a/src/PJ_mbtfpq.c +++ /dev/null @@ -1,74 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(mbtfpq, "McBryde-Thomas Flat-Polar Quartic") "\n\tCyl, Sph"; - -#define NITER 20 -#define EPS 1e-7 -#define ONETOL 1.000001 -#define C 1.70710678118654752440 -#define RC 0.58578643762690495119 -#define FYC 1.87475828462269495505 -#define RYC 0.53340209679417701685 -#define FXC 0.31245971410378249250 -#define RXC 3.20041258076506210122 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double th1, c; - int i; - (void) P; - - c = C * sin(lp.phi); - for (i = NITER; i; --i) { - lp.phi -= th1 = (sin(.5*lp.phi) + sin(lp.phi) - c) / - (.5*cos(.5*lp.phi) + cos(lp.phi)); - if (fabs(th1) < EPS) break; - } - xy.x = FXC * lp.lam * (1.0 + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); - xy.y = FYC * sin(0.5 * lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double t; - - lp.phi = RYC * xy.y; - if (fabs(lp.phi) > 1.) { - if (fabs(lp.phi) > ONETOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - else if (lp.phi < 0.) { t = -1.; lp.phi = -M_PI; } - else { t = 1.; lp.phi = M_PI; } - } else - lp.phi = 2. * asin(t = lp.phi); - lp.lam = RXC * xy.x / (1. + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); - lp.phi = RC * (t + sin(lp.phi)); - if (fabs(lp.phi) > 1.) - if (fabs(lp.phi) > ONETOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - else lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - else - lp.phi = asin(lp.phi); - return lp; -} - - -PJ *PROJECTION(mbtfpq) { - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_mbtfpq.cpp b/src/PJ_mbtfpq.cpp new file mode 100644 index 00000000..b7c0eb16 --- /dev/null +++ b/src/PJ_mbtfpq.cpp @@ -0,0 +1,74 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(mbtfpq, "McBryde-Thomas Flat-Polar Quartic") "\n\tCyl, Sph"; + +#define NITER 20 +#define EPS 1e-7 +#define ONETOL 1.000001 +#define C 1.70710678118654752440 +#define RC 0.58578643762690495119 +#define FYC 1.87475828462269495505 +#define RYC 0.53340209679417701685 +#define FXC 0.31245971410378249250 +#define RXC 3.20041258076506210122 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double th1, c; + int i; + (void) P; + + c = C * sin(lp.phi); + for (i = NITER; i; --i) { + lp.phi -= th1 = (sin(.5*lp.phi) + sin(lp.phi) - c) / + (.5*cos(.5*lp.phi) + cos(lp.phi)); + if (fabs(th1) < EPS) break; + } + xy.x = FXC * lp.lam * (1.0 + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); + xy.y = FYC * sin(0.5 * lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + lp.phi = RYC * xy.y; + if (fabs(lp.phi) > 1.) { + if (fabs(lp.phi) > ONETOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + else if (lp.phi < 0.) { t = -1.; lp.phi = -M_PI; } + else { t = 1.; lp.phi = M_PI; } + } else + lp.phi = 2. * asin(t = lp.phi); + lp.lam = RXC * xy.x / (1. + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); + lp.phi = RC * (t + sin(lp.phi)); + if (fabs(lp.phi) > 1.) + if (fabs(lp.phi) > ONETOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + else lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + else + lp.phi = asin(lp.phi); + return lp; +} + + +PJ *PROJECTION(mbtfpq) { + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_merc.c b/src/PJ_merc.c deleted file mode 100644 index 1998234e..00000000 --- a/src/PJ_merc.c +++ /dev/null @@ -1,101 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "proj.h" -#include "proj_math.h" -#include "projects.h" - -PROJ_HEAD(merc, "Mercator") "\n\tCyl, Sph&Ell\n\tlat_ts="; -PROJ_HEAD(webmerc, "Web Mercator / Pseudo Mercator") "\n\tCyl, Ell\n\t"; - -#define EPS10 1.e-10 -static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */ - if (fabs(x) <= DBL_EPSILON) { - /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */ - return log1p(x); - } - return log(tan(M_FORTPI + .5 * x)); -} - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = P->k0 * lp.lam; - xy.y = - P->k0 * log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; -} - xy.x = P->k0 * lp.lam; - xy.y = P->k0 * logtanpfpim1(lp.phi); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - if ((lp.phi = pj_phi2(P->ctx, exp(- xy.y / P->k0), P->e)) == HUGE_VAL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; -} - lp.lam = xy.x / P->k0; - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = atan(sinh(xy.y / P->k0)); - lp.lam = xy.x / P->k0; - return lp; -} - - -PJ *PROJECTION(merc) { - double phits=0.0; - int is_phits; - - if( (is_phits = pj_param(P->ctx, P->params, "tlat_ts").i) ) { - phits = fabs(pj_param(P->ctx, P->params, "rlat_ts").f); - if (phits >= M_HALFPI) - return pj_default_destructor(P, PJD_ERR_LAT_TS_LARGER_THAN_90); - } - - if (P->es != 0.0) { /* ellipsoid */ - if (is_phits) - P->k0 = pj_msfn(sin(phits), cos(phits), P->es); - P->inv = e_inverse; - P->fwd = e_forward; - } - - else { /* sphere */ - if (is_phits) - P->k0 = cos(phits); - P->inv = s_inverse; - P->fwd = s_forward; - } - - return P; -} - -PJ *PROJECTION(webmerc) { - - /* Overriding k_0 with fixed parameter */ - P->k0 = 1.0; - - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_merc.cpp b/src/PJ_merc.cpp new file mode 100644 index 00000000..1998234e --- /dev/null +++ b/src/PJ_merc.cpp @@ -0,0 +1,101 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "proj.h" +#include "proj_math.h" +#include "projects.h" + +PROJ_HEAD(merc, "Mercator") "\n\tCyl, Sph&Ell\n\tlat_ts="; +PROJ_HEAD(webmerc, "Web Mercator / Pseudo Mercator") "\n\tCyl, Ell\n\t"; + +#define EPS10 1.e-10 +static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */ + if (fabs(x) <= DBL_EPSILON) { + /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */ + return log1p(x); + } + return log(tan(M_FORTPI + .5 * x)); +} + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = P->k0 * lp.lam; + xy.y = - P->k0 * log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; +} + xy.x = P->k0 * lp.lam; + xy.y = P->k0 * logtanpfpim1(lp.phi); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + if ((lp.phi = pj_phi2(P->ctx, exp(- xy.y / P->k0), P->e)) == HUGE_VAL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; +} + lp.lam = xy.x / P->k0; + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = atan(sinh(xy.y / P->k0)); + lp.lam = xy.x / P->k0; + return lp; +} + + +PJ *PROJECTION(merc) { + double phits=0.0; + int is_phits; + + if( (is_phits = pj_param(P->ctx, P->params, "tlat_ts").i) ) { + phits = fabs(pj_param(P->ctx, P->params, "rlat_ts").f); + if (phits >= M_HALFPI) + return pj_default_destructor(P, PJD_ERR_LAT_TS_LARGER_THAN_90); + } + + if (P->es != 0.0) { /* ellipsoid */ + if (is_phits) + P->k0 = pj_msfn(sin(phits), cos(phits), P->es); + P->inv = e_inverse; + P->fwd = e_forward; + } + + else { /* sphere */ + if (is_phits) + P->k0 = cos(phits); + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} + +PJ *PROJECTION(webmerc) { + + /* Overriding k_0 with fixed parameter */ + P->k0 = 1.0; + + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_mill.c b/src/PJ_mill.c deleted file mode 100644 index 3ea9636f..00000000 --- a/src/PJ_mill.c +++ /dev/null @@ -1,37 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(mill, "Miller Cylindrical") "\n\tCyl, Sph"; - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = lp.lam; - xy.y = log(tan(M_FORTPI + lp.phi * .4)) * 1.25; - - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - (void) P; - - lp.lam = xy.x; - lp.phi = 2.5 * (atan(exp(.8 * xy.y)) - M_FORTPI); - - return (lp); -} - - -PJ *PROJECTION(mill) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_mill.cpp b/src/PJ_mill.cpp new file mode 100644 index 00000000..3ea9636f --- /dev/null +++ b/src/PJ_mill.cpp @@ -0,0 +1,37 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(mill, "Miller Cylindrical") "\n\tCyl, Sph"; + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = lp.lam; + xy.y = log(tan(M_FORTPI + lp.phi * .4)) * 1.25; + + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = xy.x; + lp.phi = 2.5 * (atan(exp(.8 * xy.y)) - M_FORTPI); + + return (lp); +} + + +PJ *PROJECTION(mill) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_misrsom.c b/src/PJ_misrsom.c deleted file mode 100644 index 0308fc42..00000000 --- a/src/PJ_misrsom.c +++ /dev/null @@ -1,217 +0,0 @@ -/****************************************************************************** - * This implements Space Oblique Mercator (SOM) projection, used by the - * Multi-angle Imaging SpectroRadiometer (MISR) products, from the NASA EOS Terra - * platform. - * - * The code is identical to that of Landsat SOM (PJ_lsat.c) with the following - * parameter changes: - * - * inclination angle = 98.30382 degrees - * period of revolution = 98.88 minutes - * ascending longitude = 129.3056 degrees - (360 / 233) * path_number - * - * and the following code change: - * - * Q->rlm = PI * (1. / 248. + .5161290322580645); - * - * changed to: - * - * Q->rlm = 0 - * - *****************************************************************************/ -/* based upon Snyder and Linck, USGS-NMD */ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(misrsom, "Space oblique for MISR") - "\n\tCyl, Sph&Ell\n\tpath="; - -#define TOL 1e-7 - -struct pj_opaque { - double a2, a4, b, c1, c3; - double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; -}; - -static void seraz0(double lam, double mult, PJ *P) { - struct pj_opaque *Q = P->opaque; - double sdsq, h, s, fc, sd, sq, d__1; - - lam *= DEG_TO_RAD; - sd = sin(lam); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) / (( - 1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - d__1 = 1. + Q->q * sdsq; - h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + - Q->w * sdsq) / (d__1 * d__1) - Q->p22 * Q->ca); - sq = sqrt(Q->xj * Q->xj + s * s); - Q->b += fc = mult * (h * Q->xj - s * s) / sq; - Q->a2 += fc * cos(lam + lam); - Q->a4 += fc * cos(lam * 4.); - fc = mult * s * (h + Q->xj) / sq; - Q->c1 += fc * cos(lam); - Q->c3 += fc * cos(lam * 3.); -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int l, nn; - double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph; - double lamtp, cl, sd, sp, sav, tanphi; - - if (lp.phi > M_HALFPI) - lp.phi = M_HALFPI; - else if (lp.phi < -M_HALFPI) - lp.phi = -M_HALFPI; - if (lp.phi >= 0. ) - lampp = M_HALFPI; - else - lampp = M_PI_HALFPI; - tanphi = tan(lp.phi); - for (nn = 0;;) { - double fac; - sav = lampp; - lamtp = lp.lam + Q->p22 * lampp; - cl = cos(lamtp); - if( cl < 0 ) - fac = lampp + sin(lampp) * M_HALFPI; - else - fac = lampp - sin(lampp) * M_HALFPI; - for (l = 50; l; --l) { - lamt = lp.lam + Q->p22 * sav; - if (fabs(c = cos(lamt)) < TOL) - lamt -= TOL; - xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; - lamdp = atan(xlam) + fac; - if (fabs(fabs(sav) - fabs(lamdp)) < TOL) - break; - sav = lamdp; - } - if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) - break; - if (lamdp <= Q->rlm) - lampp = M_TWOPI_HALFPI; - else if (lamdp >= Q->rlm2) - lampp = M_HALFPI; - } - if (l) { - sp = sin(lp.phi); - phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * - sin(lamt)) / sqrt(1. - P->es * sp * sp)); - tanph = log(tan(M_FORTPI + .5 * phidp)); - sd = sin(lamdp); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) - / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - d = sqrt(Q->xj * Q->xj + s * s); - xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * - sin(lamdp * 4.) - tanph * s / d; - xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; - } else - xy.x = xy.y = HUGE_VAL; - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int nn; - double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; - - lamdp = xy.x / Q->b; - nn = 50; - do { - sav = lamdp; - sd = sin(lamdp); - sdsq = sd * sd; - s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) - / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); - lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( - 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( - Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); - lamdp /= Q->b; - } while (fabs(lamdp - sav) >= TOL && --nn); - sl = sin(lamdp); - fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - - Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); - phidp = 2. * (atan(fac) - M_FORTPI); - dd = sl * sl; - if (fabs(cos(lamdp)) < TOL) - lamdp -= TOL; - spp = sin(phidp); - sppsq = spp * spp; - lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * - Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( - 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq - * (1. + Q->u))); - sl = lamt >= 0. ? 1. : -1.; - scl = cos(lamdp) >= 0. ? 1. : -1; - lamt -= M_HALFPI * (1. - scl) * sl; - lp.lam = lamt - Q->p22 * lamdp; - if (fabs(Q->sa) < TOL) - lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); - else - lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / - (P->one_es * Q->sa)); - return lp; -} - - -PJ *PROJECTION(misrsom) { - int path; - double lam, alf, esc, ess; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - path = pj_param(P->ctx, P->params, "ipath").i; - if (path <= 0 || path > 233) - return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE); - - P->lam0 = DEG_TO_RAD * 129.3056 - M_TWOPI / 233. * path; - alf = 98.30382 * DEG_TO_RAD; - Q->p22 = 98.88 / 1440.0; - - Q->sa = sin(alf); - Q->ca = cos(alf); - if (fabs(Q->ca) < 1e-9) - Q->ca = 1e-9; - esc = P->es * Q->ca * Q->ca; - ess = P->es * Q->sa * Q->sa; - Q->w = (1. - esc) * P->rone_es; - Q->w = Q->w * Q->w - 1.; - Q->q = ess * P->rone_es; - Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; - Q->u = esc * P->rone_es; - Q->xj = P->one_es * P->one_es * P->one_es; - Q->rlm = 0; - Q->rlm2 = Q->rlm + M_TWOPI; - Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; - seraz0(0., 1., P); - for (lam = 9.; lam <= 81.0001; lam += 18.) - seraz0(lam, 4., P); - for (lam = 18; lam <= 72.0001; lam += 18.) - seraz0(lam, 2., P); - seraz0(90., 1., P); - Q->a2 /= 30.; - Q->a4 /= 60.; - Q->b /= 30.; - Q->c1 /= 15.; - Q->c3 /= 45.; - - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_misrsom.cpp b/src/PJ_misrsom.cpp new file mode 100644 index 00000000..537172c1 --- /dev/null +++ b/src/PJ_misrsom.cpp @@ -0,0 +1,217 @@ +/****************************************************************************** + * This implements Space Oblique Mercator (SOM) projection, used by the + * Multi-angle Imaging SpectroRadiometer (MISR) products, from the NASA EOS Terra + * platform. + * + * The code is identical to that of Landsat SOM (PJ_lsat.c) with the following + * parameter changes: + * + * inclination angle = 98.30382 degrees + * period of revolution = 98.88 minutes + * ascending longitude = 129.3056 degrees - (360 / 233) * path_number + * + * and the following code change: + * + * Q->rlm = PI * (1. / 248. + .5161290322580645); + * + * changed to: + * + * Q->rlm = 0 + * + *****************************************************************************/ +/* based upon Snyder and Linck, USGS-NMD */ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(misrsom, "Space oblique for MISR") + "\n\tCyl, Sph&Ell\n\tpath="; + +#define TOL 1e-7 + +struct pj_opaque { + double a2, a4, b, c1, c3; + double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; +}; + +static void seraz0(double lam, double mult, PJ *P) { + struct pj_opaque *Q = static_cast(P->opaque); + double sdsq, h, s, fc, sd, sq, d__1; + + lam *= DEG_TO_RAD; + sd = sin(lam); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) / (( + 1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d__1 = 1. + Q->q * sdsq; + h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + + Q->w * sdsq) / (d__1 * d__1) - Q->p22 * Q->ca); + sq = sqrt(Q->xj * Q->xj + s * s); + Q->b += fc = mult * (h * Q->xj - s * s) / sq; + Q->a2 += fc * cos(lam + lam); + Q->a4 += fc * cos(lam * 4.); + fc = mult * s * (h + Q->xj) / sq; + Q->c1 += fc * cos(lam); + Q->c3 += fc * cos(lam * 3.); +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int l, nn; + double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph; + double lamtp, cl, sd, sp, sav, tanphi; + + if (lp.phi > M_HALFPI) + lp.phi = M_HALFPI; + else if (lp.phi < -M_HALFPI) + lp.phi = -M_HALFPI; + if (lp.phi >= 0. ) + lampp = M_HALFPI; + else + lampp = M_PI_HALFPI; + tanphi = tan(lp.phi); + for (nn = 0;;) { + double fac; + sav = lampp; + lamtp = lp.lam + Q->p22 * lampp; + cl = cos(lamtp); + if( cl < 0 ) + fac = lampp + sin(lampp) * M_HALFPI; + else + fac = lampp - sin(lampp) * M_HALFPI; + for (l = 50; l; --l) { + lamt = lp.lam + Q->p22 * sav; + if (fabs(c = cos(lamt)) < TOL) + lamt -= TOL; + xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; + lamdp = atan(xlam) + fac; + if (fabs(fabs(sav) - fabs(lamdp)) < TOL) + break; + sav = lamdp; + } + if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) + break; + if (lamdp <= Q->rlm) + lampp = M_TWOPI_HALFPI; + else if (lamdp >= Q->rlm2) + lampp = M_HALFPI; + } + if (l) { + sp = sin(lp.phi); + phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * + sin(lamt)) / sqrt(1. - P->es * sp * sp)); + tanph = log(tan(M_FORTPI + .5 * phidp)); + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d = sqrt(Q->xj * Q->xj + s * s); + xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * + sin(lamdp * 4.) - tanph * s / d; + xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; + } else + xy.x = xy.y = HUGE_VAL; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int nn; + double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; + + lamdp = xy.x / Q->b; + nn = 50; + do { + sav = lamdp; + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( + 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( + Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); + lamdp /= Q->b; + } while (fabs(lamdp - sav) >= TOL && --nn); + sl = sin(lamdp); + fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - + Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); + phidp = 2. * (atan(fac) - M_FORTPI); + dd = sl * sl; + if (fabs(cos(lamdp)) < TOL) + lamdp -= TOL; + spp = sin(phidp); + sppsq = spp * spp; + lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * + Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( + 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq + * (1. + Q->u))); + sl = lamt >= 0. ? 1. : -1.; + scl = cos(lamdp) >= 0. ? 1. : -1; + lamt -= M_HALFPI * (1. - scl) * sl; + lp.lam = lamt - Q->p22 * lamdp; + if (fabs(Q->sa) < TOL) + lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); + else + lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / + (P->one_es * Q->sa)); + return lp; +} + + +PJ *PROJECTION(misrsom) { + int path; + double lam, alf, esc, ess; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + path = pj_param(P->ctx, P->params, "ipath").i; + if (path <= 0 || path > 233) + return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE); + + P->lam0 = DEG_TO_RAD * 129.3056 - M_TWOPI / 233. * path; + alf = 98.30382 * DEG_TO_RAD; + Q->p22 = 98.88 / 1440.0; + + Q->sa = sin(alf); + Q->ca = cos(alf); + if (fabs(Q->ca) < 1e-9) + Q->ca = 1e-9; + esc = P->es * Q->ca * Q->ca; + ess = P->es * Q->sa * Q->sa; + Q->w = (1. - esc) * P->rone_es; + Q->w = Q->w * Q->w - 1.; + Q->q = ess * P->rone_es; + Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; + Q->u = esc * P->rone_es; + Q->xj = P->one_es * P->one_es * P->one_es; + Q->rlm = 0; + Q->rlm2 = Q->rlm + M_TWOPI; + Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; + seraz0(0., 1., P); + for (lam = 9.; lam <= 81.0001; lam += 18.) + seraz0(lam, 4., P); + for (lam = 18; lam <= 72.0001; lam += 18.) + seraz0(lam, 2., P); + seraz0(90., 1., P); + Q->a2 /= 30.; + Q->a4 /= 60.; + Q->b /= 30.; + Q->c1 /= 15.; + Q->c3 /= 45.; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_mod_ster.c b/src/PJ_mod_ster.c deleted file mode 100644 index 5e6ce136..00000000 --- a/src/PJ_mod_ster.c +++ /dev/null @@ -1,280 +0,0 @@ -/* based upon Snyder and Linck, USGS-NMD */ -#define PJ_LIB__ -#include -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(mil_os, "Miller Oblated Stereographic") "\n\tAzi(mod)"; -PROJ_HEAD(lee_os, "Lee Oblated Stereographic") "\n\tAzi(mod)"; -PROJ_HEAD(gs48, "Mod. Stereographic of 48 U.S.") "\n\tAzi(mod)"; -PROJ_HEAD(alsk, "Mod. Stereographic of Alaska") "\n\tAzi(mod)"; -PROJ_HEAD(gs50, "Mod. Stereographic of 50 U.S.") "\n\tAzi(mod)"; - -#define EPSLN 1e-12 - -struct pj_opaque { - const COMPLEX *zcoeff; \ - double cchio, schio; \ - int n; -}; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sinlon, coslon, esphi, chi, schi, cchi, s; - COMPLEX p; - - sinlon = sin(lp.lam); - coslon = cos(lp.lam); - esphi = P->e * sin(lp.phi); - chi = 2. * atan(tan((M_HALFPI + lp.phi) * .5) * - pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI; - schi = sin(chi); - cchi = cos(chi); - s = 2. / (1. + Q->schio * schi + Q->cchio * cchi * coslon); - p.r = s * cchi * sinlon; - p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon); - p = pj_zpoly1(p, Q->zcoeff, Q->n); - xy.x = p.r; - xy.y = p.i; - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - int nn; - COMPLEX p, fxy, fpxy, dp; - double den, rh = 0.0, z, sinz = 0.0, cosz = 0.0, chi, phi = 0.0, esphi; - - p.r = xy.x; - p.i = xy.y; - for (nn = 20; nn ;--nn) { - fxy = pj_zpolyd1(p, Q->zcoeff, Q->n, &fpxy); - fxy.r -= xy.x; - fxy.i -= xy.y; - den = fpxy.r * fpxy.r + fpxy.i * fpxy.i; - dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den; - dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den; - p.r += dp.r; - p.i += dp.i; - if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) - break; - } - if (nn) { - rh = hypot(p.r, p.i); - z = 2. * atan(.5 * rh); - sinz = sin(z); - cosz = cos(z); - lp.lam = P->lam0; - if (fabs(rh) <= EPSLN) { - /* if we end up here input coordinates were (0,0). - * pj_inv() adds P->lam0 to lp.lam, this way we are - * sure to get the correct offset */ - lp.lam = 0.0; - lp.phi = P->phi0; - return lp; - } - chi = aasin(P->ctx, cosz * Q->schio + p.i * sinz * Q->cchio / rh); - phi = chi; - for (nn = 20; nn ;--nn) { - double dphi; - esphi = P->e * sin(phi); - dphi = 2. * atan(tan((M_HALFPI + chi) * .5) * - pow((1. + esphi) / (1. - esphi), P->e * .5)) - M_HALFPI - phi; - phi += dphi; - if (fabs(dphi) <= EPSLN) - break; - } - } - if (nn) { - lp.phi = phi; - lp.lam = atan2(p.r * sinz, rh * Q->cchio * cosz - p.i * - Q->schio * sinz); - } else - lp.lam = lp.phi = HUGE_VAL; - return lp; -} - - -static PJ *setup(PJ *P) { /* general initialization */ - struct pj_opaque *Q = P->opaque; - double esphi, chio; - - if (P->es != 0.0) { - esphi = P->e * sin(P->phi0); - chio = 2. * atan(tan((M_HALFPI + P->phi0) * .5) * - pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI; - } else - chio = P->phi0; - Q->schio = sin(chio); - Q->cchio = cos(chio); - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} - - -/* Miller Oblated Stereographic */ -PJ *PROJECTION(mil_os) { - static const COMPLEX AB[] = { - {0.924500, 0.}, - {0., 0.}, - {0.019430, 0.} - }; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = 2; - P->lam0 = DEG_TO_RAD * 20.; - P->phi0 = DEG_TO_RAD * 18.; - Q->zcoeff = AB; - P->es = 0.; - - return setup(P); -} - - -/* Lee Oblated Stereographic */ -PJ *PROJECTION(lee_os) { - static const COMPLEX AB[] = { - {0.721316, 0.}, - {0., 0.}, - {-0.0088162, -0.00617325} - }; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = 2; - P->lam0 = DEG_TO_RAD * -165.; - P->phi0 = DEG_TO_RAD * -10.; - Q->zcoeff = AB; - P->es = 0.; - - return setup(P); -} - - -PJ *PROJECTION(gs48) { - static const COMPLEX /* 48 United States */ - AB[] = { - {0.98879, 0.}, - {0., 0.}, - {-0.050909, 0.}, - {0., 0.}, - {0.075528, 0.} - }; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = 4; - P->lam0 = DEG_TO_RAD * -96.; - P->phi0 = DEG_TO_RAD * 39.; - Q->zcoeff = AB; - P->es = 0.; - P->a = 6370997.; - - return setup(P); -} - - -PJ *PROJECTION(alsk) { - static const COMPLEX ABe[] = { /* Alaska ellipsoid */ - { .9945303, 0.}, - { .0052083, -.0027404}, - { .0072721, .0048181}, - {-.0151089, -.1932526}, - { .0642675, -.1381226}, - { .3582802, -.2884586}, - }; - - static const COMPLEX ABs[] = { /* Alaska sphere */ - { .9972523, 0.}, - { .0052513, -.0041175}, - { .0074606, .0048125}, - {-.0153783, -.1968253}, - { .0636871, -.1408027}, - { .3660976, -.2937382} - }; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = 5; - P->lam0 = DEG_TO_RAD * -152.; - P->phi0 = DEG_TO_RAD * 64.; - if (P->es != 0.0) { /* fixed ellipsoid/sphere */ - Q->zcoeff = ABe; - P->a = 6378206.4; - P->e = sqrt(P->es = 0.00676866); - } else { - Q->zcoeff = ABs; - P->a = 6370997.; - } - - return setup(P); -} - - -PJ *PROJECTION(gs50) { - static const COMPLEX ABe[] = { /* GS50 ellipsoid */ - { .9827497, 0.}, - { .0210669, .0053804}, - {-.1031415, -.0571664}, - {-.0323337, -.0322847}, - { .0502303, .1211983}, - { .0251805, .0895678}, - {-.0012315, -.1416121}, - { .0072202, -.1317091}, - {-.0194029, .0759677}, - {-.0210072, .0834037} - }; - - static const COMPLEX ABs[] = { /* GS50 sphere */ - { .9842990, 0.}, - { .0211642, .0037608}, - {-.1036018, -.0575102}, - {-.0329095, -.0320119}, - { .0499471, .1223335}, - { .0260460, .0899805}, - { .0007388, -.1435792}, - { .0075848, -.1334108}, - {-.0216473, .0776645}, - {-.0225161, .0853673} - }; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->n = 9; - P->lam0 = DEG_TO_RAD * -120.; - P->phi0 = DEG_TO_RAD * 45.; - if (P->es != 0.0) { /* fixed ellipsoid/sphere */ - Q->zcoeff = ABe; - P->a = 6378206.4; - P->e = sqrt(P->es = 0.00676866); - } else { - Q->zcoeff = ABs; - P->a = 6370997.; - } - - return setup(P); -} - diff --git a/src/PJ_mod_ster.cpp b/src/PJ_mod_ster.cpp new file mode 100644 index 00000000..ad5c9cdb --- /dev/null +++ b/src/PJ_mod_ster.cpp @@ -0,0 +1,280 @@ +/* based upon Snyder and Linck, USGS-NMD */ +#define PJ_LIB__ +#include +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(mil_os, "Miller Oblated Stereographic") "\n\tAzi(mod)"; +PROJ_HEAD(lee_os, "Lee Oblated Stereographic") "\n\tAzi(mod)"; +PROJ_HEAD(gs48, "Mod. Stereographic of 48 U.S.") "\n\tAzi(mod)"; +PROJ_HEAD(alsk, "Mod. Stereographic of Alaska") "\n\tAzi(mod)"; +PROJ_HEAD(gs50, "Mod. Stereographic of 50 U.S.") "\n\tAzi(mod)"; + +#define EPSLN 1e-12 + +struct pj_opaque { + const COMPLEX *zcoeff; \ + double cchio, schio; \ + int n; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sinlon, coslon, esphi, chi, schi, cchi, s; + COMPLEX p; + + sinlon = sin(lp.lam); + coslon = cos(lp.lam); + esphi = P->e * sin(lp.phi); + chi = 2. * atan(tan((M_HALFPI + lp.phi) * .5) * + pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI; + schi = sin(chi); + cchi = cos(chi); + s = 2. / (1. + Q->schio * schi + Q->cchio * cchi * coslon); + p.r = s * cchi * sinlon; + p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon); + p = pj_zpoly1(p, Q->zcoeff, Q->n); + xy.x = p.r; + xy.y = p.i; + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + int nn; + COMPLEX p, fxy, fpxy, dp; + double den, rh = 0.0, z, sinz = 0.0, cosz = 0.0, chi, phi = 0.0, esphi; + + p.r = xy.x; + p.i = xy.y; + for (nn = 20; nn ;--nn) { + fxy = pj_zpolyd1(p, Q->zcoeff, Q->n, &fpxy); + fxy.r -= xy.x; + fxy.i -= xy.y; + den = fpxy.r * fpxy.r + fpxy.i * fpxy.i; + dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den; + dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den; + p.r += dp.r; + p.i += dp.i; + if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) + break; + } + if (nn) { + rh = hypot(p.r, p.i); + z = 2. * atan(.5 * rh); + sinz = sin(z); + cosz = cos(z); + lp.lam = P->lam0; + if (fabs(rh) <= EPSLN) { + /* if we end up here input coordinates were (0,0). + * pj_inv() adds P->lam0 to lp.lam, this way we are + * sure to get the correct offset */ + lp.lam = 0.0; + lp.phi = P->phi0; + return lp; + } + chi = aasin(P->ctx, cosz * Q->schio + p.i * sinz * Q->cchio / rh); + phi = chi; + for (nn = 20; nn ;--nn) { + double dphi; + esphi = P->e * sin(phi); + dphi = 2. * atan(tan((M_HALFPI + chi) * .5) * + pow((1. + esphi) / (1. - esphi), P->e * .5)) - M_HALFPI - phi; + phi += dphi; + if (fabs(dphi) <= EPSLN) + break; + } + } + if (nn) { + lp.phi = phi; + lp.lam = atan2(p.r * sinz, rh * Q->cchio * cosz - p.i * + Q->schio * sinz); + } else + lp.lam = lp.phi = HUGE_VAL; + return lp; +} + + +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = static_cast(P->opaque); + double esphi, chio; + + if (P->es != 0.0) { + esphi = P->e * sin(P->phi0); + chio = 2. * atan(tan((M_HALFPI + P->phi0) * .5) * + pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI; + } else + chio = P->phi0; + Q->schio = sin(chio); + Q->cchio = cos(chio); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +/* Miller Oblated Stereographic */ +PJ *PROJECTION(mil_os) { + static const COMPLEX AB[] = { + {0.924500, 0.}, + {0., 0.}, + {0.019430, 0.} + }; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = 2; + P->lam0 = DEG_TO_RAD * 20.; + P->phi0 = DEG_TO_RAD * 18.; + Q->zcoeff = AB; + P->es = 0.; + + return setup(P); +} + + +/* Lee Oblated Stereographic */ +PJ *PROJECTION(lee_os) { + static const COMPLEX AB[] = { + {0.721316, 0.}, + {0., 0.}, + {-0.0088162, -0.00617325} + }; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = 2; + P->lam0 = DEG_TO_RAD * -165.; + P->phi0 = DEG_TO_RAD * -10.; + Q->zcoeff = AB; + P->es = 0.; + + return setup(P); +} + + +PJ *PROJECTION(gs48) { + static const COMPLEX /* 48 United States */ + AB[] = { + {0.98879, 0.}, + {0., 0.}, + {-0.050909, 0.}, + {0., 0.}, + {0.075528, 0.} + }; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = 4; + P->lam0 = DEG_TO_RAD * -96.; + P->phi0 = DEG_TO_RAD * 39.; + Q->zcoeff = AB; + P->es = 0.; + P->a = 6370997.; + + return setup(P); +} + + +PJ *PROJECTION(alsk) { + static const COMPLEX ABe[] = { /* Alaska ellipsoid */ + { .9945303, 0.}, + { .0052083, -.0027404}, + { .0072721, .0048181}, + {-.0151089, -.1932526}, + { .0642675, -.1381226}, + { .3582802, -.2884586}, + }; + + static const COMPLEX ABs[] = { /* Alaska sphere */ + { .9972523, 0.}, + { .0052513, -.0041175}, + { .0074606, .0048125}, + {-.0153783, -.1968253}, + { .0636871, -.1408027}, + { .3660976, -.2937382} + }; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = 5; + P->lam0 = DEG_TO_RAD * -152.; + P->phi0 = DEG_TO_RAD * 64.; + if (P->es != 0.0) { /* fixed ellipsoid/sphere */ + Q->zcoeff = ABe; + P->a = 6378206.4; + P->e = sqrt(P->es = 0.00676866); + } else { + Q->zcoeff = ABs; + P->a = 6370997.; + } + + return setup(P); +} + + +PJ *PROJECTION(gs50) { + static const COMPLEX ABe[] = { /* GS50 ellipsoid */ + { .9827497, 0.}, + { .0210669, .0053804}, + {-.1031415, -.0571664}, + {-.0323337, -.0322847}, + { .0502303, .1211983}, + { .0251805, .0895678}, + {-.0012315, -.1416121}, + { .0072202, -.1317091}, + {-.0194029, .0759677}, + {-.0210072, .0834037} + }; + + static const COMPLEX ABs[] = { /* GS50 sphere */ + { .9842990, 0.}, + { .0211642, .0037608}, + {-.1036018, -.0575102}, + {-.0329095, -.0320119}, + { .0499471, .1223335}, + { .0260460, .0899805}, + { .0007388, -.1435792}, + { .0075848, -.1334108}, + {-.0216473, .0776645}, + {-.0225161, .0853673} + }; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->n = 9; + P->lam0 = DEG_TO_RAD * -120.; + P->phi0 = DEG_TO_RAD * 45.; + if (P->es != 0.0) { /* fixed ellipsoid/sphere */ + Q->zcoeff = ABe; + P->a = 6378206.4; + P->e = sqrt(P->es = 0.00676866); + } else { + Q->zcoeff = ABs; + P->a = 6370997.; + } + + return setup(P); +} + diff --git a/src/PJ_moll.c b/src/PJ_moll.c deleted file mode 100644 index 4ac73841..00000000 --- a/src/PJ_moll.c +++ /dev/null @@ -1,110 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(moll, "Mollweide") "\n\tPCyl, Sph"; -PROJ_HEAD(wag4, "Wagner IV") "\n\tPCyl, Sph"; -PROJ_HEAD(wag5, "Wagner V") "\n\tPCyl, Sph"; - -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 - -struct pj_opaque { - double C_x, C_y, C_p; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double k, V; - int i; - - k = Q->C_p * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin(lp.phi) - k) / - (1. + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) - lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; - else - lp.phi *= 0.5; - xy.x = Q->C_x * lp.lam * cos(lp.phi); - xy.y = Q->C_y * sin(lp.phi); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - lp.phi = aasin(P->ctx, xy.y / Q->C_y); - lp.lam = xy.x / (Q->C_x * cos(lp.phi)); - if (fabs(lp.lam) < M_PI) { - lp.phi += lp.phi; - lp.phi = aasin(P->ctx, (lp.phi + sin(lp.phi)) / Q->C_p); - } else { - lp.lam = lp.phi = HUGE_VAL; - } - return lp; -} - - -static PJ * setup(PJ *P, double p) { - struct pj_opaque *Q = P->opaque; - double r, sp, p2 = p + p; - - P->es = 0; - sp = sin(p); - r = sqrt(M_TWOPI * sp / (p2 + sin(p2))); - - Q->C_x = 2. * r / M_PI; - Q->C_y = r / sp; - Q->C_p = p2 + sin(p2); - - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} - - -PJ *PROJECTION(moll) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - return setup(P, M_HALFPI); -} - - -PJ *PROJECTION(wag4) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - return setup(P, M_PI/3.); -} - -PJ *PROJECTION(wag5) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - P->es = 0; - Q->C_x = 0.90977; - Q->C_y = 1.65014; - Q->C_p = 3.00896; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_moll.cpp b/src/PJ_moll.cpp new file mode 100644 index 00000000..ed7e946d --- /dev/null +++ b/src/PJ_moll.cpp @@ -0,0 +1,110 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(moll, "Mollweide") "\n\tPCyl, Sph"; +PROJ_HEAD(wag4, "Wagner IV") "\n\tPCyl, Sph"; +PROJ_HEAD(wag5, "Wagner V") "\n\tPCyl, Sph"; + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + +struct pj_opaque { + double C_x, C_y, C_p; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double k, V; + int i; + + k = Q->C_p * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (lp.phi + sin(lp.phi) - k) / + (1. + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) + lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; + else + lp.phi *= 0.5; + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.y = Q->C_y * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + lp.phi = aasin(P->ctx, xy.y / Q->C_y); + lp.lam = xy.x / (Q->C_x * cos(lp.phi)); + if (fabs(lp.lam) < M_PI) { + lp.phi += lp.phi; + lp.phi = aasin(P->ctx, (lp.phi + sin(lp.phi)) / Q->C_p); + } else { + lp.lam = lp.phi = HUGE_VAL; + } + return lp; +} + + +static PJ * setup(PJ *P, double p) { + struct pj_opaque *Q = static_cast(P->opaque); + double r, sp, p2 = p + p; + + P->es = 0; + sp = sin(p); + r = sqrt(M_TWOPI * sp / (p2 + sin(p2))); + + Q->C_x = 2. * r / M_PI; + Q->C_y = r / sp; + Q->C_p = p2 + sin(p2); + + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(moll) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + return setup(P, M_HALFPI); +} + + +PJ *PROJECTION(wag4) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + return setup(P, M_PI/3.); +} + +PJ *PROJECTION(wag5) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + P->es = 0; + Q->C_x = 0.90977; + Q->C_y = 1.65014; + Q->C_p = 3.00896; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_molodensky.c b/src/PJ_molodensky.c deleted file mode 100644 index dbc83768..00000000 --- a/src/PJ_molodensky.c +++ /dev/null @@ -1,327 +0,0 @@ -/*********************************************************************** - - (Abridged) Molodensky Transform - - Kristian Evers, 2017-07-07 - -************************************************************************ - - Implements the (abridged) Molodensky transformations for 2D and 3D - data. - - Primarily useful for implementation of datum shifts in transformation - pipelines. - - The code in this file is mostly based on - - The Standard and Abridged Molodensky Coordinate Transformation - Formulae, 2004, R.E. Deaking, - http://www.mygeodesy.id.au/documents/Molodensky%20V2.pdf - - - -************************************************************************ -* Copyright (c) 2017, Kristian Evers / SDFE -* -* 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. -* -***********************************************************************/ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(molodensky, "Molodensky transform"); - -static XYZ forward_3d(LPZ lpz, PJ *P); -static LPZ reverse_3d(XYZ xyz, PJ *P); - -struct pj_opaque_molodensky { - double dx; - double dy; - double dz; - double da; - double df; - int abridged; -}; - - -static double RN (double a, double es, double phi) { -/********************************************************** - N(phi) - prime vertical radius of curvature - ------------------------------------------- - - This is basically the same function as in PJ_cart.c - should probably be refactored into it's own file at some - point. - -**********************************************************/ - double s = sin(phi); - if (es==0) - return a; - - return a / sqrt (1 - es*s*s); -} - - -static double RM (double a, double es, double phi) { -/********************************************************** - M(phi) - Meridian radius of curvature - ------------------------------------- - - Source: - - E.J Krakiwsky & D.B. Thomson, 1974, - GEODETIC POSITION COMPUTATIONS, - - Fredericton NB, Canada: - University of New Brunswick, - Department of Geodesy and Geomatics Engineering, - Lecture Notes No. 39, - 99 pp. - - http://www2.unb.ca/gge/Pubs/LN39.pdf - -**********************************************************/ - double s = sin(phi); - if (es==0) - return a; - - /* eq. 13a */ - if (phi == 0) - return a * (1-es); - - /* eq. 13b */ - if (fabs(phi) == M_PI_2) - return a / sqrt(1-es); - - /* eq. 13 */ - return (a * (1 - es) ) / pow(1 - es*s*s, 1.5); - -} - - -static LPZ calc_standard_params(LPZ lpz, PJ *P) { - struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; - double dphi, dlam, dh; - - /* sines and cosines */ - double slam = sin(lpz.lam); - double clam = cos(lpz.lam); - double sphi = sin(lpz.phi); - double cphi = cos(lpz.phi); - - /* ellipsoid parameters and differences */ - double f = P->f, a = P->a; - double dx = Q->dx, dy = Q->dy, dz = Q->dz; - double da = Q->da, df = Q->df; - - /* ellipsoid radii of curvature */ - double rho = RM(a, P->es, lpz.phi); - double nu = RN(a, P->e2s, lpz.phi); - - /* delta phi */ - dphi = (-dx*sphi*clam) - (dy*sphi*slam) + (dz*cphi) - + ((nu * P->es * sphi * cphi * da) / a) - + (sphi*cphi * ( rho/(1-f) + nu*(1-f))*df); - dphi /= (rho + lpz.z); - - /* delta lambda */ - dlam = (-dx*slam + dy*clam) / ((nu+lpz.z)*cphi); - - /* delta h */ - dh = dx*cphi*clam + dy*cphi*slam + dz*sphi - (a/nu)*da + nu*(1-f)*sphi*sphi*df; - - lpz.phi = dphi; - lpz.lam = dlam; - lpz.z = dh; - - return lpz; -} - - -static LPZ calc_abridged_params(LPZ lpz, PJ *P) { - struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; - double dphi, dlam, dh; - - /* sines and cosines */ - double slam = sin(lpz.lam); - double clam = cos(lpz.lam); - double sphi = sin(lpz.phi); - double cphi = cos(lpz.phi); - - /* ellipsoid parameters and differences */ - double dx = Q->dx, dy = Q->dy, dz = Q->dz; - double da = Q->da, df = Q->df; - double adffda = (P->a*df + P->f*da); - - /* delta phi */ - dphi = -dx*sphi*clam - dy*sphi*slam + dz*cphi + adffda*sin(2*lpz.phi); - dphi /= RM(P->a, P->es, lpz.phi); - - /* delta lambda */ - dlam = -dx*slam + dy*clam; - dlam /= RN(P->a, P->e2s, lpz.phi)*cphi; - - /* delta h */ - dh = dx*cphi*clam + dy*cphi*slam + dz*sphi - da + adffda*sphi*sphi; - - /* offset coordinate */ - lpz.phi = dphi; - lpz.lam = dlam; - lpz.z = dh; - - return lpz; -} - - -static XY forward_2d(LP lp, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - - point.lp = lp; - point.xyz = forward_3d(point.lpz, P); - - return point.xy; -} - - -static LP reverse_2d(XY xy, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - - point.xy = xy; - point.xyz.z = 0; - point.lpz = reverse_3d(point.xyz, P); - - return point.lp; -} - - -static XYZ forward_3d(LPZ lpz, PJ *P) { - struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - - point.lpz = lpz; - - /* calculate parameters depending on the mode we are in */ - if (Q->abridged) { - lpz = calc_abridged_params(lpz, P); - } else { - lpz = calc_standard_params(lpz, P); - } - - /* offset coordinate */ - point.lpz.phi += lpz.phi; - point.lpz.lam += lpz.lam; - point.lpz.z += lpz.z; - - return point.xyz; -} - - -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - obs.xyz = forward_3d(obs.lpz, P); - return obs; -} - - -static LPZ reverse_3d(XYZ xyz, PJ *P) { - struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - LPZ lpz; - - /* calculate parameters depending on the mode we are in */ - point.xyz = xyz; - if (Q->abridged) - lpz = calc_abridged_params(point.lpz, P); - else - lpz = calc_standard_params(point.lpz, P); - - /* offset coordinate */ - point.lpz.phi -= lpz.phi; - point.lpz.lam -= lpz.lam; - point.lpz.z -= lpz.z; - - return point.lpz; -} - - -static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - obs.lpz = reverse_3d(obs.xyz, P); - return obs; -} - - -PJ *TRANSFORMATION(molodensky,1) { - int count_required_params = 0; - struct pj_opaque_molodensky *Q = pj_calloc(1, sizeof(struct pj_opaque_molodensky)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = (void *) Q; - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = forward_2d; - P->inv = reverse_2d; - - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - - /* read args */ - if (pj_param(P->ctx, P->params, "tdx").i) { - count_required_params ++; - Q->dx = pj_param(P->ctx, P->params, "ddx").f; - } - - if (pj_param(P->ctx, P->params, "tdy").i) { - count_required_params ++; - Q->dy = pj_param(P->ctx, P->params, "ddy").f; - } - - if (pj_param(P->ctx, P->params, "tdz").i) { - count_required_params ++; - Q->dz = pj_param(P->ctx, P->params, "ddz").f; - } - - if (pj_param(P->ctx, P->params, "tda").i) { - count_required_params ++; - Q->da = pj_param(P->ctx, P->params, "dda").f; - } - - if (pj_param(P->ctx, P->params, "tdf").i) { - count_required_params ++; - Q->df = pj_param(P->ctx, P->params, "ddf").f; - } - - Q->abridged = pj_param(P->ctx, P->params, "tabridged").i; - - /* We want all parameters (except +abridged) to be set */ - if (count_required_params == 0) - return pj_default_destructor(P, PJD_ERR_NO_ARGS); - - if (count_required_params != 5) - return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); - - return P; -} diff --git a/src/PJ_molodensky.cpp b/src/PJ_molodensky.cpp new file mode 100644 index 00000000..6b231081 --- /dev/null +++ b/src/PJ_molodensky.cpp @@ -0,0 +1,327 @@ +/*********************************************************************** + + (Abridged) Molodensky Transform + + Kristian Evers, 2017-07-07 + +************************************************************************ + + Implements the (abridged) Molodensky transformations for 2D and 3D + data. + + Primarily useful for implementation of datum shifts in transformation + pipelines. + + The code in this file is mostly based on + + The Standard and Abridged Molodensky Coordinate Transformation + Formulae, 2004, R.E. Deaking, + http://www.mygeodesy.id.au/documents/Molodensky%20V2.pdf + + + +************************************************************************ +* Copyright (c) 2017, Kristian Evers / SDFE +* +* 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. +* +***********************************************************************/ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(molodensky, "Molodensky transform"); + +static XYZ forward_3d(LPZ lpz, PJ *P); +static LPZ reverse_3d(XYZ xyz, PJ *P); + +struct pj_opaque_molodensky { + double dx; + double dy; + double dz; + double da; + double df; + int abridged; +}; + + +static double RN (double a, double es, double phi) { +/********************************************************** + N(phi) - prime vertical radius of curvature + ------------------------------------------- + + This is basically the same function as in PJ_cart.c + should probably be refactored into it's own file at some + point. + +**********************************************************/ + double s = sin(phi); + if (es==0) + return a; + + return a / sqrt (1 - es*s*s); +} + + +static double RM (double a, double es, double phi) { +/********************************************************** + M(phi) - Meridian radius of curvature + ------------------------------------- + + Source: + + E.J Krakiwsky & D.B. Thomson, 1974, + GEODETIC POSITION COMPUTATIONS, + + Fredericton NB, Canada: + University of New Brunswick, + Department of Geodesy and Geomatics Engineering, + Lecture Notes No. 39, + 99 pp. + + http://www2.unb.ca/gge/Pubs/LN39.pdf + +**********************************************************/ + double s = sin(phi); + if (es==0) + return a; + + /* eq. 13a */ + if (phi == 0) + return a * (1-es); + + /* eq. 13b */ + if (fabs(phi) == M_PI_2) + return a / sqrt(1-es); + + /* eq. 13 */ + return (a * (1 - es) ) / pow(1 - es*s*s, 1.5); + +} + + +static LPZ calc_standard_params(LPZ lpz, PJ *P) { + struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; + double dphi, dlam, dh; + + /* sines and cosines */ + double slam = sin(lpz.lam); + double clam = cos(lpz.lam); + double sphi = sin(lpz.phi); + double cphi = cos(lpz.phi); + + /* ellipsoid parameters and differences */ + double f = P->f, a = P->a; + double dx = Q->dx, dy = Q->dy, dz = Q->dz; + double da = Q->da, df = Q->df; + + /* ellipsoid radii of curvature */ + double rho = RM(a, P->es, lpz.phi); + double nu = RN(a, P->e2s, lpz.phi); + + /* delta phi */ + dphi = (-dx*sphi*clam) - (dy*sphi*slam) + (dz*cphi) + + ((nu * P->es * sphi * cphi * da) / a) + + (sphi*cphi * ( rho/(1-f) + nu*(1-f))*df); + dphi /= (rho + lpz.z); + + /* delta lambda */ + dlam = (-dx*slam + dy*clam) / ((nu+lpz.z)*cphi); + + /* delta h */ + dh = dx*cphi*clam + dy*cphi*slam + dz*sphi - (a/nu)*da + nu*(1-f)*sphi*sphi*df; + + lpz.phi = dphi; + lpz.lam = dlam; + lpz.z = dh; + + return lpz; +} + + +static LPZ calc_abridged_params(LPZ lpz, PJ *P) { + struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; + double dphi, dlam, dh; + + /* sines and cosines */ + double slam = sin(lpz.lam); + double clam = cos(lpz.lam); + double sphi = sin(lpz.phi); + double cphi = cos(lpz.phi); + + /* ellipsoid parameters and differences */ + double dx = Q->dx, dy = Q->dy, dz = Q->dz; + double da = Q->da, df = Q->df; + double adffda = (P->a*df + P->f*da); + + /* delta phi */ + dphi = -dx*sphi*clam - dy*sphi*slam + dz*cphi + adffda*sin(2*lpz.phi); + dphi /= RM(P->a, P->es, lpz.phi); + + /* delta lambda */ + dlam = -dx*slam + dy*clam; + dlam /= RN(P->a, P->e2s, lpz.phi)*cphi; + + /* delta h */ + dh = dx*cphi*clam + dy*cphi*slam + dz*sphi - da + adffda*sphi*sphi; + + /* offset coordinate */ + lpz.phi = dphi; + lpz.lam = dlam; + lpz.z = dh; + + return lpz; +} + + +static XY forward_2d(LP lp, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + + point.lp = lp; + point.xyz = forward_3d(point.lpz, P); + + return point.xy; +} + + +static LP reverse_2d(XY xy, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + + point.xy = xy; + point.xyz.z = 0; + point.lpz = reverse_3d(point.xyz, P); + + return point.lp; +} + + +static XYZ forward_3d(LPZ lpz, PJ *P) { + struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + + point.lpz = lpz; + + /* calculate parameters depending on the mode we are in */ + if (Q->abridged) { + lpz = calc_abridged_params(lpz, P); + } else { + lpz = calc_standard_params(lpz, P); + } + + /* offset coordinate */ + point.lpz.phi += lpz.phi; + point.lpz.lam += lpz.lam; + point.lpz.z += lpz.z; + + return point.xyz; +} + + +static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { + obs.xyz = forward_3d(obs.lpz, P); + return obs; +} + + +static LPZ reverse_3d(XYZ xyz, PJ *P) { + struct pj_opaque_molodensky *Q = (struct pj_opaque_molodensky *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + LPZ lpz; + + /* calculate parameters depending on the mode we are in */ + point.xyz = xyz; + if (Q->abridged) + lpz = calc_abridged_params(point.lpz, P); + else + lpz = calc_standard_params(point.lpz, P); + + /* offset coordinate */ + point.lpz.phi -= lpz.phi; + point.lpz.lam -= lpz.lam; + point.lpz.z -= lpz.z; + + return point.lpz; +} + + +static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { + obs.lpz = reverse_3d(obs.xyz, P); + return obs; +} + + +PJ *TRANSFORMATION(molodensky,1) { + int count_required_params = 0; + struct pj_opaque_molodensky *Q = static_cast(pj_calloc(1, sizeof(struct pj_opaque_molodensky))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = (void *) Q; + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = forward_2d; + P->inv = reverse_2d; + + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + + /* read args */ + if (pj_param(P->ctx, P->params, "tdx").i) { + count_required_params ++; + Q->dx = pj_param(P->ctx, P->params, "ddx").f; + } + + if (pj_param(P->ctx, P->params, "tdy").i) { + count_required_params ++; + Q->dy = pj_param(P->ctx, P->params, "ddy").f; + } + + if (pj_param(P->ctx, P->params, "tdz").i) { + count_required_params ++; + Q->dz = pj_param(P->ctx, P->params, "ddz").f; + } + + if (pj_param(P->ctx, P->params, "tda").i) { + count_required_params ++; + Q->da = pj_param(P->ctx, P->params, "dda").f; + } + + if (pj_param(P->ctx, P->params, "tdf").i) { + count_required_params ++; + Q->df = pj_param(P->ctx, P->params, "ddf").f; + } + + Q->abridged = pj_param(P->ctx, P->params, "tabridged").i; + + /* We want all parameters (except +abridged) to be set */ + if (count_required_params == 0) + return pj_default_destructor(P, PJD_ERR_NO_ARGS); + + if (count_required_params != 5) + return pj_default_destructor(P, PJD_ERR_MISSING_ARGS); + + return P; +} diff --git a/src/PJ_natearth.c b/src/PJ_natearth.c deleted file mode 100644 index 27a6b137..00000000 --- a/src/PJ_natearth.c +++ /dev/null @@ -1,100 +0,0 @@ -/* -The Natural Earth projection was designed by Tom Patterson, US National Park -Service, in 2007, using Flex Projector. The shape of the original projection -was defined at every 5 degrees and piece-wise cubic spline interpolation was -used to compute the complete graticule. -The code here uses polynomial functions instead of cubic splines and -is therefore much simpler to program. The polynomial approximation was -developed by Bojan Savric, in collaboration with Tom Patterson and Bernhard -Jenny, Institute of Cartography, ETH Zurich. It slightly deviates from -Patterson's original projection by adding additional curvature to meridians -where they meet the horizontal pole line. This improvement is by intention -and designed in collaboration with Tom Patterson. -Port to PROJ.4 by Bernhard Jenny, 6 June 2011 -*/ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl, Sph"; - -#define A0 0.8707 -#define A1 -0.131979 -#define A2 -0.013791 -#define A3 0.003971 -#define A4 -0.001529 -#define B0 1.007226 -#define B1 0.015085 -#define B2 -0.044475 -#define B3 0.028874 -#define B4 -0.005916 -#define C0 B0 -#define C1 (3 * B1) -#define C2 (7 * B2) -#define C3 (9 * B3) -#define C4 (11 * B4) -#define EPS 1e-11 -#define MAX_Y (0.8707 * 0.52 * M_PI) -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double phi2, phi4; - (void) P; - - phi2 = lp.phi * lp.phi; - phi4 = phi2 * phi2; - xy.x = lp.lam * (A0 + phi2 * (A1 + phi2 * (A2 + phi4 * phi2 * (A3 + phi2 * A4)))); - xy.y = lp.phi * (B0 + phi2 * (B1 + phi4 * (B2 + B3 * phi2 + B4 * phi4))); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double yc, tol, y2, y4, f, fder; - int i; - (void) P; - - /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } - - /* latitude */ - yc = xy.y; - for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ - y2 = yc * yc; - y4 = y2 * y2; - f = (yc * (B0 + y2 * (B1 + y4 * (B2 + B3 * y2 + B4 * y4)))) - xy.y; - fder = C0 + y2 * (C1 + y4 * (C2 + C3 * y2 + C4 * y4)); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - if( i == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - lp.phi = yc; - - /* longitude */ - y2 = yc * yc; - lp.lam = xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4)))); - - return lp; -} - - -PJ *PROJECTION(natearth) { - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_natearth.cpp b/src/PJ_natearth.cpp new file mode 100644 index 00000000..27a6b137 --- /dev/null +++ b/src/PJ_natearth.cpp @@ -0,0 +1,100 @@ +/* +The Natural Earth projection was designed by Tom Patterson, US National Park +Service, in 2007, using Flex Projector. The shape of the original projection +was defined at every 5 degrees and piece-wise cubic spline interpolation was +used to compute the complete graticule. +The code here uses polynomial functions instead of cubic splines and +is therefore much simpler to program. The polynomial approximation was +developed by Bojan Savric, in collaboration with Tom Patterson and Bernhard +Jenny, Institute of Cartography, ETH Zurich. It slightly deviates from +Patterson's original projection by adding additional curvature to meridians +where they meet the horizontal pole line. This improvement is by intention +and designed in collaboration with Tom Patterson. +Port to PROJ.4 by Bernhard Jenny, 6 June 2011 +*/ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl, Sph"; + +#define A0 0.8707 +#define A1 -0.131979 +#define A2 -0.013791 +#define A3 0.003971 +#define A4 -0.001529 +#define B0 1.007226 +#define B1 0.015085 +#define B2 -0.044475 +#define B3 0.028874 +#define B4 -0.005916 +#define C0 B0 +#define C1 (3 * B1) +#define C2 (7 * B2) +#define C3 (9 * B3) +#define C4 (11 * B4) +#define EPS 1e-11 +#define MAX_Y (0.8707 * 0.52 * M_PI) +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2, phi4; + (void) P; + + phi2 = lp.phi * lp.phi; + phi4 = phi2 * phi2; + xy.x = lp.lam * (A0 + phi2 * (A1 + phi2 * (A2 + phi4 * phi2 * (A3 + phi2 * A4)))); + xy.y = lp.phi * (B0 + phi2 * (B1 + phi4 * (B2 + B3 * phi2 + B4 * phi4))); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, y4, f, fder; + int i; + (void) P; + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + /* latitude */ + yc = xy.y; + for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ + y2 = yc * yc; + y4 = y2 * y2; + f = (yc * (B0 + y2 * (B1 + y4 * (B2 + B3 * y2 + B4 * y4)))) - xy.y; + fder = C0 + y2 * (C1 + y4 * (C2 + C3 * y2 + C4 * y4)); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + if( i == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + lp.phi = yc; + + /* longitude */ + y2 = yc * yc; + lp.lam = xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4)))); + + return lp; +} + + +PJ *PROJECTION(natearth) { + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_natearth2.c b/src/PJ_natearth2.c deleted file mode 100644 index f6aba671..00000000 --- a/src/PJ_natearth2.c +++ /dev/null @@ -1,97 +0,0 @@ -/* -The Natural Earth II projection was designed by Tom Patterson, US National -Park Service, in 2012, using Flex Projector. The polynomial equation was -developed by Bojan Savric and Bernhard Jenny, College of Earth, Ocean, -and Atmospheric Sciences, Oregon State University. -Port to PROJ.4 by Bojan Savric, 4 April 2016 -*/ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(natearth2, "Natural Earth 2") "\n\tPCyl, Sph"; - -#define A0 0.84719 -#define A1 -0.13063 -#define A2 -0.04515 -#define A3 0.05494 -#define A4 -0.02326 -#define A5 0.00331 -#define B0 1.01183 -#define B1 -0.02625 -#define B2 0.01926 -#define B3 -0.00396 -#define C0 B0 -#define C1 (9 * B1) -#define C2 (11 * B2) -#define C3 (13 * B3) -#define EPS 1e-11 -#define MAX_Y (0.84719 * 0.535117535153096 * M_PI) -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double phi2, phi4, phi6; - (void) P; - - phi2 = lp.phi * lp.phi; - phi4 = phi2 * phi2; - phi6 = phi2 * phi4; - - xy.x = lp.lam * (A0 + A1 * phi2 + phi6 * phi6 * (A2 + A3 * phi2 + A4 * phi4 + A5 * phi6)); - xy.y = lp.phi * (B0 + phi4 * phi4 * (B1 + B2 * phi2 + B3 * phi4)); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double yc, tol, y2, y4, y6, f, fder; - int i; - (void) P; - - /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } - - /* latitude */ - yc = xy.y; - for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ - y2 = yc * yc; - y4 = y2 * y2; - f = (yc * (B0 + y4 * y4 * (B1 + B2 * y2 + B3 * y4))) - xy.y; - fder = C0 + y4 * y4 * (C1 + C2 * y2 + C3 * y4); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - if( i == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - lp.phi = yc; - - /* longitude */ - y2 = yc * yc; - y4 = y2 * y2; - y6 = y2 * y4; - - lp.lam = xy.x / (A0 + A1 * y2 + y6 * y6 * (A2 + A3 * y2 + A4 * y4 + A5 * y6)); - - return lp; -} - - -PJ *PROJECTION(natearth2) { - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_natearth2.cpp b/src/PJ_natearth2.cpp new file mode 100644 index 00000000..f6aba671 --- /dev/null +++ b/src/PJ_natearth2.cpp @@ -0,0 +1,97 @@ +/* +The Natural Earth II projection was designed by Tom Patterson, US National +Park Service, in 2012, using Flex Projector. The polynomial equation was +developed by Bojan Savric and Bernhard Jenny, College of Earth, Ocean, +and Atmospheric Sciences, Oregon State University. +Port to PROJ.4 by Bojan Savric, 4 April 2016 +*/ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(natearth2, "Natural Earth 2") "\n\tPCyl, Sph"; + +#define A0 0.84719 +#define A1 -0.13063 +#define A2 -0.04515 +#define A3 0.05494 +#define A4 -0.02326 +#define A5 0.00331 +#define B0 1.01183 +#define B1 -0.02625 +#define B2 0.01926 +#define B3 -0.00396 +#define C0 B0 +#define C1 (9 * B1) +#define C2 (11 * B2) +#define C3 (13 * B3) +#define EPS 1e-11 +#define MAX_Y (0.84719 * 0.535117535153096 * M_PI) +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2, phi4, phi6; + (void) P; + + phi2 = lp.phi * lp.phi; + phi4 = phi2 * phi2; + phi6 = phi2 * phi4; + + xy.x = lp.lam * (A0 + A1 * phi2 + phi6 * phi6 * (A2 + A3 * phi2 + A4 * phi4 + A5 * phi6)); + xy.y = lp.phi * (B0 + phi4 * phi4 * (B1 + B2 * phi2 + B3 * phi4)); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, y4, y6, f, fder; + int i; + (void) P; + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + /* latitude */ + yc = xy.y; + for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ + y2 = yc * yc; + y4 = y2 * y2; + f = (yc * (B0 + y4 * y4 * (B1 + B2 * y2 + B3 * y4))) - xy.y; + fder = C0 + y4 * y4 * (C1 + C2 * y2 + C3 * y4); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + if( i == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + lp.phi = yc; + + /* longitude */ + y2 = yc * yc; + y4 = y2 * y2; + y6 = y2 * y4; + + lp.lam = xy.x / (A0 + A1 * y2 + y6 * y6 * (A2 + A3 * y2 + A4 * y4 + A5 * y6)); + + return lp; +} + + +PJ *PROJECTION(natearth2) { + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_nell.c b/src/PJ_nell.c deleted file mode 100644 index 2a7ea32c..00000000 --- a/src/PJ_nell.c +++ /dev/null @@ -1,51 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(nell, "Nell") "\n\tPCyl, Sph"; - -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double k, V; - int i; - (void) P; - - k = 2. * sin(lp.phi); - V = lp.phi * lp.phi; - lp.phi *= 1.00371 + V * (-0.0935382 + V * -0.011412); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin(lp.phi) - k) / - (1. + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); - xy.y = lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.lam = 2. * xy.x / (1. + cos(xy.y)); - lp.phi = aasin(P->ctx,0.5 * (xy.y + sin(xy.y))); - - return lp; -} - - -PJ *PROJECTION(nell) { - - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_nell.cpp b/src/PJ_nell.cpp new file mode 100644 index 00000000..2a7ea32c --- /dev/null +++ b/src/PJ_nell.cpp @@ -0,0 +1,51 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(nell, "Nell") "\n\tPCyl, Sph"; + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double k, V; + int i; + (void) P; + + k = 2. * sin(lp.phi); + V = lp.phi * lp.phi; + lp.phi *= 1.00371 + V * (-0.0935382 + V * -0.011412); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (lp.phi + sin(lp.phi) - k) / + (1. + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); + xy.y = lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.lam = 2. * xy.x / (1. + cos(xy.y)); + lp.phi = aasin(P->ctx,0.5 * (xy.y + sin(xy.y))); + + return lp; +} + + +PJ *PROJECTION(nell) { + + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_nell_h.c b/src/PJ_nell_h.c deleted file mode 100644 index 28c3ace7..00000000 --- a/src/PJ_nell_h.c +++ /dev/null @@ -1,53 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(nell_h, "Nell-Hammer") "\n\tPCyl, Sph"; - -#define NITER 9 -#define EPS 1e-7 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); - xy.y = 2.0 * (lp.phi - tan(0.5 *lp.phi)); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double V, c, p; - int i; - (void) P; - - p = 0.5 * xy.y; - for (i = NITER; i ; --i) { - c = cos(0.5 * lp.phi); - lp.phi -= V = (lp.phi - tan(lp.phi/2) - p)/(1. - 0.5/(c*c)); - if (fabs(V) < EPS) - break; - } - if (!i) { - lp.phi = p < 0. ? -M_HALFPI : M_HALFPI; - lp.lam = 2. * xy.x; - } else - lp.lam = 2. * xy.x / (1. + cos(lp.phi)); - - return lp; -} - - -PJ *PROJECTION(nell_h) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_nell_h.cpp b/src/PJ_nell_h.cpp new file mode 100644 index 00000000..28c3ace7 --- /dev/null +++ b/src/PJ_nell_h.cpp @@ -0,0 +1,53 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(nell_h, "Nell-Hammer") "\n\tPCyl, Sph"; + +#define NITER 9 +#define EPS 1e-7 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); + xy.y = 2.0 * (lp.phi - tan(0.5 *lp.phi)); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double V, c, p; + int i; + (void) P; + + p = 0.5 * xy.y; + for (i = NITER; i ; --i) { + c = cos(0.5 * lp.phi); + lp.phi -= V = (lp.phi - tan(lp.phi/2) - p)/(1. - 0.5/(c*c)); + if (fabs(V) < EPS) + break; + } + if (!i) { + lp.phi = p < 0. ? -M_HALFPI : M_HALFPI; + lp.lam = 2. * xy.x; + } else + lp.lam = 2. * xy.x / (1. + cos(lp.phi)); + + return lp; +} + + +PJ *PROJECTION(nell_h) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_nocol.c b/src/PJ_nocol.c deleted file mode 100644 index 541d08b2..00000000 --- a/src/PJ_nocol.c +++ /dev/null @@ -1,54 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(nicol, "Nicolosi Globular") "\n\tMisc Sph, no inv"; - -#define EPS 1e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - (void) P; - - if (fabs(lp.lam) < EPS) { - xy.x = 0; - xy.y = lp.phi; - } else if (fabs(lp.phi) < EPS) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(fabs(lp.lam) - M_HALFPI) < EPS) { - xy.x = lp.lam * cos(lp.phi); - xy.y = M_HALFPI * sin(lp.phi); - } else if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) { - xy.x = 0; - xy.y = lp.phi; - } else { - double tb, c, d, m, n, r2, sp; - - tb = M_HALFPI / lp.lam - lp.lam / M_HALFPI; - c = lp.phi / M_HALFPI; - d = (1 - c * c)/((sp = sin(lp.phi)) - c); - r2 = tb / d; - r2 *= r2; - m = (tb * sp / d - 0.5 * tb)/(1. + r2); - n = (sp / r2 + 0.5 * d)/(1. + 1./r2); - xy.x = cos(lp.phi); - xy.x = sqrt(m * m + xy.x * xy.x / (1. + r2)); - xy.x = M_HALFPI * ( m + (lp.lam < 0. ? -xy.x : xy.x)); - xy.y = sqrt(n * n - (sp * sp / r2 + d * sp - 1.) / - (1. + 1./r2)); - xy.y = M_HALFPI * ( n + (lp.phi < 0. ? xy.y : -xy.y )); - } - return (xy); -} - - -PJ *PROJECTION(nicol) { - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_nocol.cpp b/src/PJ_nocol.cpp new file mode 100644 index 00000000..541d08b2 --- /dev/null +++ b/src/PJ_nocol.cpp @@ -0,0 +1,54 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(nicol, "Nicolosi Globular") "\n\tMisc Sph, no inv"; + +#define EPS 1e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + if (fabs(lp.lam) < EPS) { + xy.x = 0; + xy.y = lp.phi; + } else if (fabs(lp.phi) < EPS) { + xy.x = lp.lam; + xy.y = 0.; + } else if (fabs(fabs(lp.lam) - M_HALFPI) < EPS) { + xy.x = lp.lam * cos(lp.phi); + xy.y = M_HALFPI * sin(lp.phi); + } else if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) { + xy.x = 0; + xy.y = lp.phi; + } else { + double tb, c, d, m, n, r2, sp; + + tb = M_HALFPI / lp.lam - lp.lam / M_HALFPI; + c = lp.phi / M_HALFPI; + d = (1 - c * c)/((sp = sin(lp.phi)) - c); + r2 = tb / d; + r2 *= r2; + m = (tb * sp / d - 0.5 * tb)/(1. + r2); + n = (sp / r2 + 0.5 * d)/(1. + 1./r2); + xy.x = cos(lp.phi); + xy.x = sqrt(m * m + xy.x * xy.x / (1. + r2)); + xy.x = M_HALFPI * ( m + (lp.lam < 0. ? -xy.x : xy.x)); + xy.y = sqrt(n * n - (sp * sp / r2 + d * sp - 1.) / + (1. + 1./r2)); + xy.y = M_HALFPI * ( n + (lp.phi < 0. ? xy.y : -xy.y )); + } + return (xy); +} + + +PJ *PROJECTION(nicol) { + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_nsper.c b/src/PJ_nsper.c deleted file mode 100644 index 223cd75b..00000000 --- a/src/PJ_nsper.c +++ /dev/null @@ -1,198 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double height; - double sinph0; - double cosph0; - double p; - double rp; - double pn1; - double pfact; - double h; - double cg; - double sg; - double sw; - double cw; - enum Mode mode; - int tilt; -}; - -PROJ_HEAD(nsper, "Near-sided perspective") "\n\tAzi, Sph\n\th="; -PROJ_HEAD(tpers, "Tilted perspective") "\n\tAzi, Sph\n\ttilt= azi= h="; - -# define EPS10 1.e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (Q->mode) { - case OBLIQ: - xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; - break; - case EQUIT: - xy.y = cosphi * coslam; - break; - case S_POLE: - xy.y = - sinphi; - break; - case N_POLE: - xy.y = sinphi; - break; - } - if (xy.y < Q->rp) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.y = Q->pn1 / (Q->p - xy.y); - xy.x = xy.y * cosphi * sin(lp.lam); - switch (Q->mode) { - case OBLIQ: - xy.y *= (Q->cosph0 * sinphi - - Q->sinph0 * cosphi * coslam); - break; - case EQUIT: - xy.y *= sinphi; - break; - case N_POLE: - coslam = - coslam; - /*-fallthrough*/ - case S_POLE: - xy.y *= cosphi * coslam; - break; - } - if (Q->tilt) { - double yt, ba; - - yt = xy.y * Q->cg + xy.x * Q->sg; - ba = 1. / (yt * Q->sw * Q->h + Q->cw); - xy.x = (xy.x * Q->cg - xy.y * Q->sg) * Q->cw * ba; - xy.y = yt * ba; - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double rh, cosz, sinz; - - if (Q->tilt) { - double bm, bq, yt; - - yt = 1./(Q->pn1 - xy.y * Q->sw); - bm = Q->pn1 * xy.x * yt; - bq = Q->pn1 * xy.y * Q->cw * yt; - xy.x = bm * Q->cg + bq * Q->sg; - xy.y = bq * Q->cg - bm * Q->sg; - } - rh = hypot(xy.x, xy.y); - if ((sinz = 1. - rh * rh * Q->pfact) < 0.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - sinz = (Q->p - sqrt(sinz)) / (Q->pn1 / rh + rh / Q->pn1); - cosz = sqrt(1. - sinz * sinz); - if (fabs(rh) <= EPS10) { - lp.lam = 0.; - lp.phi = P->phi0; - } else { - switch (Q->mode) { - case OBLIQ: - lp.phi = asin(cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh); - xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; - xy.x *= sinz * Q->cosph0; - break; - case EQUIT: - lp.phi = asin(xy.y * sinz / rh); - xy.y = cosz * rh; - xy.x *= sinz; - break; - case N_POLE: - lp.phi = asin(cosz); - xy.y = -xy.y; - break; - case S_POLE: - lp.phi = - asin(cosz); - break; - } - lp.lam = atan2(xy.x, xy.y); - } - return lp; -} - - -static PJ *setup(PJ *P) { - struct pj_opaque *Q = P->opaque; - - if ((Q->height = pj_param(P->ctx, P->params, "dh").f) <= 0.) - return pj_default_destructor(P, PJD_ERR_H_LESS_THAN_ZERO); - - if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(P->phi0) < EPS10) - Q->mode = EQUIT; - else { - Q->mode = OBLIQ; - Q->sinph0 = sin(P->phi0); - Q->cosph0 = cos(P->phi0); - } - Q->pn1 = Q->height / P->a; /* normalize by radius */ - Q->p = 1. + Q->pn1; - Q->rp = 1. / Q->p; - Q->h = 1. / Q->pn1; - Q->pfact = (Q->p + 1.) * Q->h; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} - - -PJ *PROJECTION(nsper) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->tilt = 0; - - return setup(P); -} - - -PJ *PROJECTION(tpers) { - double omega, gamma; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - omega = pj_param(P->ctx, P->params, "rtilt").f; - gamma = pj_param(P->ctx, P->params, "razi").f; - Q->tilt = 1; - Q->cg = cos(gamma); Q->sg = sin(gamma); - Q->cw = cos(omega); Q->sw = sin(omega); - - return setup(P); -} - diff --git a/src/PJ_nsper.cpp b/src/PJ_nsper.cpp new file mode 100644 index 00000000..e6ecb852 --- /dev/null +++ b/src/PJ_nsper.cpp @@ -0,0 +1,198 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double height; + double sinph0; + double cosph0; + double p; + double rp; + double pn1; + double pfact; + double h; + double cg; + double sg; + double sw; + double cw; + enum Mode mode; + int tilt; +}; + +PROJ_HEAD(nsper, "Near-sided perspective") "\n\tAzi, Sph\n\th="; +PROJ_HEAD(tpers, "Tilted perspective") "\n\tAzi, Sph\n\ttilt= azi= h="; + +# define EPS10 1.e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; + break; + case EQUIT: + xy.y = cosphi * coslam; + break; + case S_POLE: + xy.y = - sinphi; + break; + case N_POLE: + xy.y = sinphi; + break; + } + if (xy.y < Q->rp) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.y = Q->pn1 / (Q->p - xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + switch (Q->mode) { + case OBLIQ: + xy.y *= (Q->cosph0 * sinphi - + Q->sinph0 * cosphi * coslam); + break; + case EQUIT: + xy.y *= sinphi; + break; + case N_POLE: + coslam = - coslam; + /*-fallthrough*/ + case S_POLE: + xy.y *= cosphi * coslam; + break; + } + if (Q->tilt) { + double yt, ba; + + yt = xy.y * Q->cg + xy.x * Q->sg; + ba = 1. / (yt * Q->sw * Q->h + Q->cw); + xy.x = (xy.x * Q->cg - xy.y * Q->sg) * Q->cw * ba; + xy.y = yt * ba; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rh, cosz, sinz; + + if (Q->tilt) { + double bm, bq, yt; + + yt = 1./(Q->pn1 - xy.y * Q->sw); + bm = Q->pn1 * xy.x * yt; + bq = Q->pn1 * xy.y * Q->cw * yt; + xy.x = bm * Q->cg + bq * Q->sg; + xy.y = bq * Q->cg - bm * Q->sg; + } + rh = hypot(xy.x, xy.y); + if ((sinz = 1. - rh * rh * Q->pfact) < 0.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + sinz = (Q->p - sqrt(sinz)) / (Q->pn1 / rh + rh / Q->pn1); + cosz = sqrt(1. - sinz * sinz); + if (fabs(rh) <= EPS10) { + lp.lam = 0.; + lp.phi = P->phi0; + } else { + switch (Q->mode) { + case OBLIQ: + lp.phi = asin(cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh); + xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; + xy.x *= sinz * Q->cosph0; + break; + case EQUIT: + lp.phi = asin(xy.y * sinz / rh); + xy.y = cosz * rh; + xy.x *= sinz; + break; + case N_POLE: + lp.phi = asin(cosz); + xy.y = -xy.y; + break; + case S_POLE: + lp.phi = - asin(cosz); + break; + } + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +static PJ *setup(PJ *P) { + struct pj_opaque *Q = static_cast(P->opaque); + + if ((Q->height = pj_param(P->ctx, P->params, "dh").f) <= 0.) + return pj_default_destructor(P, PJD_ERR_H_LESS_THAN_ZERO); + + if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else if (fabs(P->phi0) < EPS10) + Q->mode = EQUIT; + else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + Q->pn1 = Q->height / P->a; /* normalize by radius */ + Q->p = 1. + Q->pn1; + Q->rp = 1. / Q->p; + Q->h = 1. / Q->pn1; + Q->pfact = (Q->p + 1.) * Q->h; + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} + + +PJ *PROJECTION(nsper) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->tilt = 0; + + return setup(P); +} + + +PJ *PROJECTION(tpers) { + double omega, gamma; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + omega = pj_param(P->ctx, P->params, "rtilt").f; + gamma = pj_param(P->ctx, P->params, "razi").f; + Q->tilt = 1; + Q->cg = cos(gamma); Q->sg = sin(gamma); + Q->cw = cos(omega); Q->sw = sin(omega); + + return setup(P); +} + diff --git a/src/PJ_nzmg.c b/src/PJ_nzmg.c deleted file mode 100644 index bf0862fb..00000000 --- a/src/PJ_nzmg.c +++ /dev/null @@ -1,123 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the nzmg (New Zealand Map Grid) projection. - * Very loosely based upon DMA code by Bradford W. Drew - * Author: Gerald Evenden - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * - * 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. - *****************************************************************************/ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(nzmg, "New Zealand Map Grid") "\n\tfixed Earth"; - -#define EPSLN 1e-10 -#define SEC5_TO_RAD 0.4848136811095359935899141023 -#define RAD_TO_SEC5 2.062648062470963551564733573 - -static const COMPLEX bf[] = { - { .7557853228, 0.0}, - { .249204646, 0.003371507}, - {-.001541739, 0.041058560}, - {-.10162907, 0.01727609}, - {-.26623489, -0.36249218}, - {-.6870983, -1.1651967} }; - -static const double tphi[] = { 1.5627014243, .5185406398, -.03333098, - -.1052906, -.0368594, .007317, - .01220, .00394, -.0013 }; - -static const double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, - -.0055161, .0026906, -.001333, .00067, -.00034 }; - -#define Nbf 5 -#define Ntpsi 9 -#define Ntphi 8 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - COMPLEX p; - const double *C; - int i; - - lp.phi = (lp.phi - P->phi0) * RAD_TO_SEC5; - for (p.r = *(C = tpsi + (i = Ntpsi)); i ; --i) - p.r = *--C + lp.phi * p.r; - p.r *= lp.phi; - p.i = lp.lam; - p = pj_zpoly1(p, bf, Nbf); - xy.x = p.i; - xy.y = p.r; - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - int nn, i; - COMPLEX p, f, fp, dp; - double den; - const double *C; - - p.r = xy.y; - p.i = xy.x; - for (nn = 20; nn ;--nn) { - f = pj_zpolyd1(p, bf, Nbf, &fp); - f.r -= xy.y; - f.i -= xy.x; - den = fp.r * fp.r + fp.i * fp.i; - p.r += dp.r = -(f.r * fp.r + f.i * fp.i) / den; - p.i += dp.i = -(f.i * fp.r - f.r * fp.i) / den; - if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) - break; - } - if (nn) { - lp.lam = p.i; - for (lp.phi = *(C = tphi + (i = Ntphi)); i ; --i) - lp.phi = *--C + p.r * lp.phi; - lp.phi = P->phi0 + p.r * lp.phi * SEC5_TO_RAD; - } else - lp.lam = lp.phi = HUGE_VAL; - - return lp; -} - - -PJ *PROJECTION(nzmg) { - /* force to International major axis */ - P->ra = 1. / (P->a = 6378388.0); - P->lam0 = DEG_TO_RAD * 173.; - P->phi0 = DEG_TO_RAD * -41.; - P->x0 = 2510000.; - P->y0 = 6023150.; - - P->inv = e_inverse; - P->fwd = e_forward; - - - return P; -} diff --git a/src/PJ_nzmg.cpp b/src/PJ_nzmg.cpp new file mode 100644 index 00000000..bf0862fb --- /dev/null +++ b/src/PJ_nzmg.cpp @@ -0,0 +1,123 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the nzmg (New Zealand Map Grid) projection. + * Very loosely based upon DMA code by Bradford W. Drew + * Author: Gerald Evenden + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * + * 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. + *****************************************************************************/ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(nzmg, "New Zealand Map Grid") "\n\tfixed Earth"; + +#define EPSLN 1e-10 +#define SEC5_TO_RAD 0.4848136811095359935899141023 +#define RAD_TO_SEC5 2.062648062470963551564733573 + +static const COMPLEX bf[] = { + { .7557853228, 0.0}, + { .249204646, 0.003371507}, + {-.001541739, 0.041058560}, + {-.10162907, 0.01727609}, + {-.26623489, -0.36249218}, + {-.6870983, -1.1651967} }; + +static const double tphi[] = { 1.5627014243, .5185406398, -.03333098, + -.1052906, -.0368594, .007317, + .01220, .00394, -.0013 }; + +static const double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, + -.0055161, .0026906, -.001333, .00067, -.00034 }; + +#define Nbf 5 +#define Ntpsi 9 +#define Ntphi 8 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + COMPLEX p; + const double *C; + int i; + + lp.phi = (lp.phi - P->phi0) * RAD_TO_SEC5; + for (p.r = *(C = tpsi + (i = Ntpsi)); i ; --i) + p.r = *--C + lp.phi * p.r; + p.r *= lp.phi; + p.i = lp.lam; + p = pj_zpoly1(p, bf, Nbf); + xy.x = p.i; + xy.y = p.r; + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + int nn, i; + COMPLEX p, f, fp, dp; + double den; + const double *C; + + p.r = xy.y; + p.i = xy.x; + for (nn = 20; nn ;--nn) { + f = pj_zpolyd1(p, bf, Nbf, &fp); + f.r -= xy.y; + f.i -= xy.x; + den = fp.r * fp.r + fp.i * fp.i; + p.r += dp.r = -(f.r * fp.r + f.i * fp.i) / den; + p.i += dp.i = -(f.i * fp.r - f.r * fp.i) / den; + if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) + break; + } + if (nn) { + lp.lam = p.i; + for (lp.phi = *(C = tphi + (i = Ntphi)); i ; --i) + lp.phi = *--C + p.r * lp.phi; + lp.phi = P->phi0 + p.r * lp.phi * SEC5_TO_RAD; + } else + lp.lam = lp.phi = HUGE_VAL; + + return lp; +} + + +PJ *PROJECTION(nzmg) { + /* force to International major axis */ + P->ra = 1. / (P->a = 6378388.0); + P->lam0 = DEG_TO_RAD * 173.; + P->phi0 = DEG_TO_RAD * -41.; + P->x0 = 2510000.; + P->y0 = 6023150.; + + P->inv = e_inverse; + P->fwd = e_forward; + + + return P; +} diff --git a/src/PJ_ob_tran.c b/src/PJ_ob_tran.c deleted file mode 100644 index f5a05cf9..00000000 --- a/src/PJ_ob_tran.c +++ /dev/null @@ -1,243 +0,0 @@ -#define PJ_LIB__ -#include -#include -#include -#include - -#include "proj.h" -#include "projects.h" - -struct pj_opaque { - struct PJconsts *link; - double lamp; - double cphip, sphip; -}; - -PROJ_HEAD(ob_tran, "General Oblique Transformation") "\n\tMisc Sph" -"\n\to_proj= plus parameters for projection" -"\n\to_lat_p= o_lon_p= (new pole) or" -"\n\to_alpha= o_lon_c= o_lat_c= or" -"\n\to_lon_1= o_lat_1= o_lon_2= o_lat_2="; - -#define TOL 1e-10 - - -static XY o_forward(LP lp, PJ *P) { /* spheroid */ - struct pj_opaque *Q = P->opaque; - double coslam, sinphi, cosphi; - - coslam = cos(lp.lam); - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam + - Q->cphip * sinphi) + Q->lamp); - lp.phi = aasin(P->ctx,Q->sphip * sinphi - Q->cphip * cosphi * coslam); - - return Q->link->fwd(lp, Q->link); -} - - -static XY t_forward(LP lp, PJ *P) { /* spheroid */ - struct pj_opaque *Q = P->opaque; - double cosphi, coslam; - - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), sin(lp.phi)) + Q->lamp); - lp.phi = aasin(P->ctx, - cosphi * coslam); - - return Q->link->fwd(lp, Q->link); -} - - -static LP o_inverse(XY xy, PJ *P) { /* spheroid */ - - struct pj_opaque *Q = P->opaque; - double coslam, sinphi, cosphi; - - LP lp = Q->link->inv(xy, Q->link); - if (lp.lam != HUGE_VAL) { - coslam = cos(lp.lam -= Q->lamp); - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - lp.phi = aasin(P->ctx,Q->sphip * sinphi + Q->cphip * cosphi * coslam); - lp.lam = aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam - - Q->cphip * sinphi); - } - return lp; -} - - -static LP t_inverse(XY xy, PJ *P) { /* spheroid */ - - struct pj_opaque *Q = P->opaque; - double cosphi, t; - - LP lp = Q->link->inv(xy, Q->link); - if (lp.lam != HUGE_VAL) { - cosphi = cos(lp.phi); - t = lp.lam - Q->lamp; - lp.lam = aatan2(cosphi * sin(t), - sin(lp.phi)); - lp.phi = aasin(P->ctx,cosphi * cos(t)); - } - return lp; -} - - -static void *destructor(PJ *P, int errlev) { - if (0==P) - return 0; - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - if (P->opaque->link) - P->opaque->link->destructor (P->opaque->link, errlev); - - return pj_default_destructor(P, errlev); -} - - - - -/*********************************************************************** - -These functions are modified versions of the functions "argc_params" -and "argv_params" from PJ_pipeline.c - -Basically, they do the somewhat backwards stunt of turning the paralist -representation of the +args back into the original +argv, +argc -representation accepted by pj_init_ctx(). - -This, however, also begs the question of whether we really need the -paralist linked list representation, or if we could do with a simpler -null-terminated argv style array? This would simplfy some code, and -keep memory allocations more localized. - -***********************************************************************/ - -typedef struct {int argc; char **argv;} ARGS; - -/* count the number of args in the linked list */ -static size_t paralist_params_argc (paralist *params) { - size_t argc = 0; - for (; params != 0; params = params->next) - argc++; - return argc; -} - - -/* turn paralist into argc/argv style argument list */ -static ARGS ob_tran_target_params (paralist *params) { - int i = 0; - ARGS args = {0, 0}; - size_t argc = paralist_params_argc (params); - if (argc < 2) - return args; - - /* all args except the proj_ob_tran */ - args.argv = pj_calloc (argc - 1, sizeof (char *)); - if (0==args.argv) - return args; - - /* Copy all args *except* the proj=ob_tran arg to the argv array */ - for (i = 0; params != 0; params = params->next) { - if (0==strcmp (params->param, "proj=ob_tran")) - continue; - args.argv[i++] = params->param; - } - args.argc = i; - - /* Then convert the o_proj=xxx element to proj=xxx */ - for (i = 0; i < args.argc; i++) { - if (0!=strncmp (args.argv[i], "o_proj=", 7)) - continue; - args.argv[i] += 2; - break; - } - - return args; -} - - - -PJ *PROJECTION(ob_tran) { - double phip; - char *name; - ARGS args; - PJ *R; /* projection to rotate */ - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return destructor(P, ENOMEM); - - P->opaque = Q; - P->destructor = destructor; - - /* get name of projection to be translated */ - if (!(name = pj_param(P->ctx, P->params, "so_proj").s)) - return destructor(P, PJD_ERR_NO_ROTATION_PROJ); - - /* avoid endless recursion */ - if( strcmp(name, "ob_tran") == 0 ) - return destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - - /* Create the target projection object to rotate */ - args = ob_tran_target_params (P->params); - R = pj_init_ctx (pj_get_ctx(P), args.argc, args.argv); - pj_dealloc (args.argv); - - if (0==R) - return destructor (P, PJD_ERR_UNKNOWN_PROJECTION_ID); - Q->link = R; - - if (pj_param(P->ctx, P->params, "to_alpha").i) { - double lamc, phic, alpha; - - lamc = pj_param(P->ctx, P->params, "ro_lon_c").f; - phic = pj_param(P->ctx, P->params, "ro_lat_c").f; - alpha = pj_param(P->ctx, P->params, "ro_alpha").f; - - if (fabs(fabs(phic) - M_HALFPI) <= TOL) - return destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90); - - Q->lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic)); - phip = aasin(P->ctx,cos(phic) * sin(alpha)); - } else if (pj_param(P->ctx, P->params, "to_lat_p").i) { /* specified new pole */ - Q->lamp = pj_param(P->ctx, P->params, "ro_lon_p").f; - phip = pj_param(P->ctx, P->params, "ro_lat_p").f; - } else { /* specified new "equator" points */ - double lam1, lam2, phi1, phi2, con; - - lam1 = pj_param(P->ctx, P->params, "ro_lon_1").f; - phi1 = pj_param(P->ctx, P->params, "ro_lat_1").f; - lam2 = pj_param(P->ctx, P->params, "ro_lon_2").f; - phi2 = pj_param(P->ctx, P->params, "ro_lat_2").f; - if (fabs(phi1 - phi2) <= TOL || (con = fabs(phi1)) <= TOL || - fabs(con - M_HALFPI) <= TOL || fabs(fabs(phi2) - M_HALFPI) <= TOL) - return destructor(P, PJD_ERR_LAT_1_OR_2_ZERO_OR_90); - - Q->lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) - - sin(phi1) * cos(phi2) * cos(lam2), - sin(phi1) * cos(phi2) * sin(lam2) - - cos(phi1) * sin(phi2) * sin(lam1)); - phip = atan(-cos(Q->lamp - lam1) / tan(phi1)); - } - - if (fabs(phip) > TOL) { /* oblique */ - Q->cphip = cos(phip); - Q->sphip = sin(phip); - P->fwd = Q->link->fwd ? o_forward : 0; - P->inv = Q->link->inv ? o_inverse : 0; - } else { /* transverse */ - P->fwd = Q->link->fwd ? t_forward : 0; - P->inv = Q->link->inv ? t_inverse : 0; - } - - /* Support some rather speculative test cases, where the rotated projection */ - /* is actually latlong. We do not want scaling in that case... */ - if (Q->link->right==PJ_IO_UNITS_ANGULAR) - P->right = PJ_IO_UNITS_PROJECTED; - - - return P; -} diff --git a/src/PJ_ob_tran.cpp b/src/PJ_ob_tran.cpp new file mode 100644 index 00000000..1a9417b8 --- /dev/null +++ b/src/PJ_ob_tran.cpp @@ -0,0 +1,243 @@ +#define PJ_LIB__ +#include +#include +#include +#include + +#include "proj.h" +#include "projects.h" + +struct pj_opaque { + struct PJconsts *link; + double lamp; + double cphip, sphip; +}; + +PROJ_HEAD(ob_tran, "General Oblique Transformation") "\n\tMisc Sph" +"\n\to_proj= plus parameters for projection" +"\n\to_lat_p= o_lon_p= (new pole) or" +"\n\to_alpha= o_lon_c= o_lat_c= or" +"\n\to_lon_1= o_lat_1= o_lon_2= o_lat_2="; + +#define TOL 1e-10 + + +static XY o_forward(LP lp, PJ *P) { /* spheroid */ + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, sinphi, cosphi; + + coslam = cos(lp.lam); + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam + + Q->cphip * sinphi) + Q->lamp); + lp.phi = aasin(P->ctx,Q->sphip * sinphi - Q->cphip * cosphi * coslam); + + return Q->link->fwd(lp, Q->link); +} + + +static XY t_forward(LP lp, PJ *P) { /* spheroid */ + struct pj_opaque *Q = static_cast(P->opaque); + double cosphi, coslam; + + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), sin(lp.phi)) + Q->lamp); + lp.phi = aasin(P->ctx, - cosphi * coslam); + + return Q->link->fwd(lp, Q->link); +} + + +static LP o_inverse(XY xy, PJ *P) { /* spheroid */ + + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, sinphi, cosphi; + + LP lp = Q->link->inv(xy, Q->link); + if (lp.lam != HUGE_VAL) { + coslam = cos(lp.lam -= Q->lamp); + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + lp.phi = aasin(P->ctx,Q->sphip * sinphi + Q->cphip * cosphi * coslam); + lp.lam = aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam - + Q->cphip * sinphi); + } + return lp; +} + + +static LP t_inverse(XY xy, PJ *P) { /* spheroid */ + + struct pj_opaque *Q = static_cast(P->opaque); + double cosphi, t; + + LP lp = Q->link->inv(xy, Q->link); + if (lp.lam != HUGE_VAL) { + cosphi = cos(lp.phi); + t = lp.lam - Q->lamp; + lp.lam = aatan2(cosphi * sin(t), - sin(lp.phi)); + lp.phi = aasin(P->ctx,cosphi * cos(t)); + } + return lp; +} + + +static PJ *destructor(PJ *P, int errlev) { + if (0==P) + return 0; + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + if (static_cast(P->opaque)->link) + static_cast(P->opaque)->link->destructor (static_cast(P->opaque)->link, errlev); + + return pj_default_destructor(P, errlev); +} + + + + +/*********************************************************************** + +These functions are modified versions of the functions "argc_params" +and "argv_params" from PJ_pipeline.c + +Basically, they do the somewhat backwards stunt of turning the paralist +representation of the +args back into the original +argv, +argc +representation accepted by pj_init_ctx(). + +This, however, also begs the question of whether we really need the +paralist linked list representation, or if we could do with a simpler +null-terminated argv style array? This would simplfy some code, and +keep memory allocations more localized. + +***********************************************************************/ + +typedef struct {int argc; char **argv;} ARGS; + +/* count the number of args in the linked list */ +static size_t paralist_params_argc (paralist *params) { + size_t argc = 0; + for (; params != 0; params = params->next) + argc++; + return argc; +} + + +/* turn paralist into argc/argv style argument list */ +static ARGS ob_tran_target_params (paralist *params) { + int i = 0; + ARGS args = {0, 0}; + size_t argc = paralist_params_argc (params); + if (argc < 2) + return args; + + /* all args except the proj_ob_tran */ + args.argv = static_cast(pj_calloc (argc - 1, sizeof (char *))); + if (0==args.argv) + return args; + + /* Copy all args *except* the proj=ob_tran arg to the argv array */ + for (i = 0; params != 0; params = params->next) { + if (0==strcmp (params->param, "proj=ob_tran")) + continue; + args.argv[i++] = params->param; + } + args.argc = i; + + /* Then convert the o_proj=xxx element to proj=xxx */ + for (i = 0; i < args.argc; i++) { + if (0!=strncmp (args.argv[i], "o_proj=", 7)) + continue; + args.argv[i] += 2; + break; + } + + return args; +} + + + +PJ *PROJECTION(ob_tran) { + double phip; + char *name; + ARGS args; + PJ *R; /* projection to rotate */ + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return destructor(P, ENOMEM); + + P->opaque = Q; + P->destructor = destructor; + + /* get name of projection to be translated */ + if (!(name = pj_param(P->ctx, P->params, "so_proj").s)) + return destructor(P, PJD_ERR_NO_ROTATION_PROJ); + + /* avoid endless recursion */ + if( strcmp(name, "ob_tran") == 0 ) + return destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + + /* Create the target projection object to rotate */ + args = ob_tran_target_params (P->params); + R = pj_init_ctx (pj_get_ctx(P), args.argc, args.argv); + pj_dealloc (args.argv); + + if (0==R) + return destructor (P, PJD_ERR_UNKNOWN_PROJECTION_ID); + Q->link = R; + + if (pj_param(P->ctx, P->params, "to_alpha").i) { + double lamc, phic, alpha; + + lamc = pj_param(P->ctx, P->params, "ro_lon_c").f; + phic = pj_param(P->ctx, P->params, "ro_lat_c").f; + alpha = pj_param(P->ctx, P->params, "ro_alpha").f; + + if (fabs(fabs(phic) - M_HALFPI) <= TOL) + return destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90); + + Q->lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic)); + phip = aasin(P->ctx,cos(phic) * sin(alpha)); + } else if (pj_param(P->ctx, P->params, "to_lat_p").i) { /* specified new pole */ + Q->lamp = pj_param(P->ctx, P->params, "ro_lon_p").f; + phip = pj_param(P->ctx, P->params, "ro_lat_p").f; + } else { /* specified new "equator" points */ + double lam1, lam2, phi1, phi2, con; + + lam1 = pj_param(P->ctx, P->params, "ro_lon_1").f; + phi1 = pj_param(P->ctx, P->params, "ro_lat_1").f; + lam2 = pj_param(P->ctx, P->params, "ro_lon_2").f; + phi2 = pj_param(P->ctx, P->params, "ro_lat_2").f; + if (fabs(phi1 - phi2) <= TOL || (con = fabs(phi1)) <= TOL || + fabs(con - M_HALFPI) <= TOL || fabs(fabs(phi2) - M_HALFPI) <= TOL) + return destructor(P, PJD_ERR_LAT_1_OR_2_ZERO_OR_90); + + Q->lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) - + sin(phi1) * cos(phi2) * cos(lam2), + sin(phi1) * cos(phi2) * sin(lam2) - + cos(phi1) * sin(phi2) * sin(lam1)); + phip = atan(-cos(Q->lamp - lam1) / tan(phi1)); + } + + if (fabs(phip) > TOL) { /* oblique */ + Q->cphip = cos(phip); + Q->sphip = sin(phip); + P->fwd = Q->link->fwd ? o_forward : 0; + P->inv = Q->link->inv ? o_inverse : 0; + } else { /* transverse */ + P->fwd = Q->link->fwd ? t_forward : 0; + P->inv = Q->link->inv ? t_inverse : 0; + } + + /* Support some rather speculative test cases, where the rotated projection */ + /* is actually latlong. We do not want scaling in that case... */ + if (Q->link->right==PJ_IO_UNITS_ANGULAR) + P->right = PJ_IO_UNITS_PROJECTED; + + + return P; +} diff --git a/src/PJ_ocea.c b/src/PJ_ocea.c deleted file mode 100644 index 7a9353a6..00000000 --- a/src/PJ_ocea.c +++ /dev/null @@ -1,100 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(ocea, "Oblique Cylindrical Equal Area") "\n\tCyl, Sph" - "lonc= alpha= or\n\tlat_1= lat_2= lon_1= lon_2="; - -struct pj_opaque { - double rok; - double rtk; - double sinphi; - double cosphi; - double singam; - double cosgam; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double t; - xy.y = sin(lp.lam); - t = cos(lp.lam); - xy.x = atan((tan(lp.phi) * Q->cosphi + Q->sinphi * xy.y) / t); - if (t < 0.) - xy.x += M_PI; - xy.x *= Q->rtk; - xy.y = Q->rok * (Q->sinphi * sin(lp.phi) - Q->cosphi * cos(lp.phi) * xy.y); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double t, s; - - xy.y /= Q->rok; - xy.x /= Q->rtk; - t = sqrt(1. - xy.y * xy.y); - lp.phi = asin(xy.y * Q->sinphi + t * Q->cosphi * (s = sin(xy.x))); - lp.lam = atan2(t * Q->sinphi * s - xy.y * Q->cosphi, - t * cos(xy.x)); - return lp; -} - - -PJ *PROJECTION(ocea) { - double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->rok = 1. / P->k0; - Q->rtk = P->k0; - /*If the keyword "alpha" is found in the sentence then use 1point+1azimuth*/ - if ( pj_param(P->ctx, P->params, "talpha").i) { - /*Define Pole of oblique transformation from 1 point & 1 azimuth*/ - alpha = pj_param(P->ctx, P->params, "ralpha").f; - lonz = pj_param(P->ctx, P->params, "rlonc").f; - /*Equation 9-8 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - Q->singam = atan(-cos(alpha)/(-sin(phi_0) * sin(alpha))) + lonz; - /*Equation 9-7 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - Q->sinphi = asin(cos(phi_0) * sin(alpha)); - /*If the keyword "alpha" is NOT found in the sentence then use 2points*/ - } else { - /*Define Pole of oblique transformation from 2 points*/ - phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; - phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; - lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; - lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; - /*Equation 9-1 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - Q->singam = atan2(cos(phi_1) * sin(phi_2) * cos(lam_1) - - sin(phi_1) * cos(phi_2) * cos(lam_2), - sin(phi_1) * cos(phi_2) * sin(lam_2) - - cos(phi_1) * sin(phi_2) * sin(lam_1) ); - - /* take care of P->lam0 wrap-around when +lam_1=-90*/ - if (lam_1 == -M_HALFPI) - Q->singam = -Q->singam; - - /*Equation 9-2 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - Q->sinphi = atan(-cos(Q->singam - lam_1) / tan(phi_1)); - } - P->lam0 = Q->singam + M_HALFPI; - Q->cosphi = cos(Q->sinphi); - Q->sinphi = sin(Q->sinphi); - Q->cosgam = cos(Q->singam); - Q->singam = sin(Q->singam); - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} diff --git a/src/PJ_ocea.cpp b/src/PJ_ocea.cpp new file mode 100644 index 00000000..81c506fe --- /dev/null +++ b/src/PJ_ocea.cpp @@ -0,0 +1,100 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(ocea, "Oblique Cylindrical Equal Area") "\n\tCyl, Sph" + "lonc= alpha= or\n\tlat_1= lat_2= lon_1= lon_2="; + +struct pj_opaque { + double rok; + double rtk; + double sinphi; + double cosphi; + double singam; + double cosgam; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t; + xy.y = sin(lp.lam); + t = cos(lp.lam); + xy.x = atan((tan(lp.phi) * Q->cosphi + Q->sinphi * xy.y) / t); + if (t < 0.) + xy.x += M_PI; + xy.x *= Q->rtk; + xy.y = Q->rok * (Q->sinphi * sin(lp.phi) - Q->cosphi * cos(lp.phi) * xy.y); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t, s; + + xy.y /= Q->rok; + xy.x /= Q->rtk; + t = sqrt(1. - xy.y * xy.y); + lp.phi = asin(xy.y * Q->sinphi + t * Q->cosphi * (s = sin(xy.x))); + lp.lam = atan2(t * Q->sinphi * s - xy.y * Q->cosphi, + t * cos(xy.x)); + return lp; +} + + +PJ *PROJECTION(ocea) { + double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->rok = 1. / P->k0; + Q->rtk = P->k0; + /*If the keyword "alpha" is found in the sentence then use 1point+1azimuth*/ + if ( pj_param(P->ctx, P->params, "talpha").i) { + /*Define Pole of oblique transformation from 1 point & 1 azimuth*/ + alpha = pj_param(P->ctx, P->params, "ralpha").f; + lonz = pj_param(P->ctx, P->params, "rlonc").f; + /*Equation 9-8 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->singam = atan(-cos(alpha)/(-sin(phi_0) * sin(alpha))) + lonz; + /*Equation 9-7 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->sinphi = asin(cos(phi_0) * sin(alpha)); + /*If the keyword "alpha" is NOT found in the sentence then use 2points*/ + } else { + /*Define Pole of oblique transformation from 2 points*/ + phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; + phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; + lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; + lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; + /*Equation 9-1 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->singam = atan2(cos(phi_1) * sin(phi_2) * cos(lam_1) - + sin(phi_1) * cos(phi_2) * cos(lam_2), + sin(phi_1) * cos(phi_2) * sin(lam_2) - + cos(phi_1) * sin(phi_2) * sin(lam_1) ); + + /* take care of P->lam0 wrap-around when +lam_1=-90*/ + if (lam_1 == -M_HALFPI) + Q->singam = -Q->singam; + + /*Equation 9-2 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->sinphi = atan(-cos(Q->singam - lam_1) / tan(phi_1)); + } + P->lam0 = Q->singam + M_HALFPI; + Q->cosphi = cos(Q->sinphi); + Q->sinphi = sin(Q->sinphi); + Q->cosgam = cos(Q->singam); + Q->singam = sin(Q->singam); + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} diff --git a/src/PJ_oea.c b/src/PJ_oea.c deleted file mode 100644 index 2bfeffa8..00000000 --- a/src/PJ_oea.c +++ /dev/null @@ -1,85 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(oea, "Oblated Equal Area") "\n\tMisc Sph\n\tn= m= theta="; - -struct pj_opaque { - double theta; - double m, n; - double two_r_m, two_r_n, rm, rn, hm, hn; - double cp0, sp0; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double Az, M, N, cp, sp, cl, shz; - - cp = cos(lp.phi); - sp = sin(lp.phi); - cl = cos(lp.lam); - Az = aatan2(cp * sin(lp.lam), Q->cp0 * sp - Q->sp0 * cp * cl) + Q->theta; - shz = sin(0.5 * aacos(P->ctx, Q->sp0 * sp + Q->cp0 * cp * cl)); - M = aasin(P->ctx, shz * sin(Az)); - N = aasin(P->ctx, shz * cos(Az) * cos(M) / cos(M * Q->two_r_m)); - xy.y = Q->n * sin(N * Q->two_r_n); - xy.x = Q->m * sin(M * Q->two_r_m) * cos(N) / cos(N * Q->two_r_n); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double N, M, xp, yp, z, Az, cz, sz, cAz; - - N = Q->hn * aasin(P->ctx,xy.y * Q->rn); - M = Q->hm * aasin(P->ctx,xy.x * Q->rm * cos(N * Q->two_r_n) / cos(N)); - xp = 2. * sin(M); - yp = 2. * sin(N) * cos(M * Q->two_r_m) / cos(M); - cAz = cos(Az = aatan2(xp, yp) - Q->theta); - z = 2. * aasin(P->ctx, 0.5 * hypot(xp, yp)); - sz = sin(z); - cz = cos(z); - lp.phi = aasin(P->ctx, Q->sp0 * cz + Q->cp0 * sz * cAz); - lp.lam = aatan2(sz * sin(Az), - Q->cp0 * cz - Q->sp0 * sz * cAz); - - return lp; -} - - - - -PJ *PROJECTION(oea) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if (((Q->n = pj_param(P->ctx, P->params, "dn").f) <= 0.) || - ((Q->m = pj_param(P->ctx, P->params, "dm").f) <= 0.)) { - return pj_default_destructor(P, PJD_ERR_INVALID_M_OR_N); - } else { - Q->theta = pj_param(P->ctx, P->params, "rtheta").f; - Q->sp0 = sin(P->phi0); - Q->cp0 = cos(P->phi0); - Q->rn = 1./ Q->n; - Q->rm = 1./ Q->m; - Q->two_r_n = 2. * Q->rn; - Q->two_r_m = 2. * Q->rm; - Q->hm = 0.5 * Q->m; - Q->hn = 0.5 * Q->n; - P->fwd = s_forward; - P->inv = s_inverse; - P->es = 0.; - } - - return P; -} - diff --git a/src/PJ_oea.cpp b/src/PJ_oea.cpp new file mode 100644 index 00000000..b39e8d5a --- /dev/null +++ b/src/PJ_oea.cpp @@ -0,0 +1,85 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(oea, "Oblated Equal Area") "\n\tMisc Sph\n\tn= m= theta="; + +struct pj_opaque { + double theta; + double m, n; + double two_r_m, two_r_n, rm, rn, hm, hn; + double cp0, sp0; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double Az, M, N, cp, sp, cl, shz; + + cp = cos(lp.phi); + sp = sin(lp.phi); + cl = cos(lp.lam); + Az = aatan2(cp * sin(lp.lam), Q->cp0 * sp - Q->sp0 * cp * cl) + Q->theta; + shz = sin(0.5 * aacos(P->ctx, Q->sp0 * sp + Q->cp0 * cp * cl)); + M = aasin(P->ctx, shz * sin(Az)); + N = aasin(P->ctx, shz * cos(Az) * cos(M) / cos(M * Q->two_r_m)); + xy.y = Q->n * sin(N * Q->two_r_n); + xy.x = Q->m * sin(M * Q->two_r_m) * cos(N) / cos(N * Q->two_r_n); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double N, M, xp, yp, z, Az, cz, sz, cAz; + + N = Q->hn * aasin(P->ctx,xy.y * Q->rn); + M = Q->hm * aasin(P->ctx,xy.x * Q->rm * cos(N * Q->two_r_n) / cos(N)); + xp = 2. * sin(M); + yp = 2. * sin(N) * cos(M * Q->two_r_m) / cos(M); + cAz = cos(Az = aatan2(xp, yp) - Q->theta); + z = 2. * aasin(P->ctx, 0.5 * hypot(xp, yp)); + sz = sin(z); + cz = cos(z); + lp.phi = aasin(P->ctx, Q->sp0 * cz + Q->cp0 * sz * cAz); + lp.lam = aatan2(sz * sin(Az), + Q->cp0 * cz - Q->sp0 * sz * cAz); + + return lp; +} + + + + +PJ *PROJECTION(oea) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if (((Q->n = pj_param(P->ctx, P->params, "dn").f) <= 0.) || + ((Q->m = pj_param(P->ctx, P->params, "dm").f) <= 0.)) { + return pj_default_destructor(P, PJD_ERR_INVALID_M_OR_N); + } else { + Q->theta = pj_param(P->ctx, P->params, "rtheta").f; + Q->sp0 = sin(P->phi0); + Q->cp0 = cos(P->phi0); + Q->rn = 1./ Q->n; + Q->rm = 1./ Q->m; + Q->two_r_n = 2. * Q->rn; + Q->two_r_m = 2. * Q->rm; + Q->hm = 0.5 * Q->m; + Q->hn = 0.5 * Q->n; + P->fwd = s_forward; + P->inv = s_inverse; + P->es = 0.; + } + + return P; +} + diff --git a/src/PJ_omerc.c b/src/PJ_omerc.c deleted file mode 100644 index 70c12e27..00000000 --- a/src/PJ_omerc.c +++ /dev/null @@ -1,227 +0,0 @@ -/* -** Copyright (c) 2003, 2006 Gerald I. Evenden -*/ -/* -** 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. -*/ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(omerc, "Oblique Mercator") - "\n\tCyl, Sph&Ell no_rot\n\t" - "alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2="; - -struct pj_opaque { - double A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot; - double v_pole_n, v_pole_s, u_0; - int no_rot; -}; - -#define TOL 1.e-7 -#define EPS 1.e-10 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double S, T, U, V, W, temp, u, v; - - if (fabs(fabs(lp.phi) - M_HALFPI) > EPS) { - W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B); - temp = 1. / W; - S = .5 * (W - temp); - T = .5 * (W + temp); - V = sin(Q->B * lp.lam); - U = (S * Q->singam - V * Q->cosgam) / T; - if (fabs(fabs(U) - 1.0) < EPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - v = 0.5 * Q->ArB * log((1. - U)/(1. + U)); - temp = cos(Q->B * lp.lam); - if(fabs(temp) < TOL) { - u = Q->A * lp.lam; - } else { - u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp); - } - } else { - v = lp.phi > 0 ? Q->v_pole_n : Q->v_pole_s; - u = Q->ArB * lp.phi; - } - if (Q->no_rot) { - xy.x = u; - xy.y = v; - } else { - u -= Q->u_0; - xy.x = v * Q->cosrot + u * Q->sinrot; - xy.y = u * Q->cosrot - v * Q->sinrot; - } - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double u, v, Qp, Sp, Tp, Vp, Up; - - if (Q->no_rot) { - v = xy.y; - u = xy.x; - } else { - v = xy.x * Q->cosrot - xy.y * Q->sinrot; - u = xy.y * Q->cosrot + xy.x * Q->sinrot + Q->u_0; - } - Qp = exp(- Q->BrA * v); - Sp = .5 * (Qp - 1. / Qp); - Tp = .5 * (Qp + 1. / Qp); - Vp = sin(Q->BrA * u); - Up = (Vp * Q->cosgam + Sp * Q->singam) / Tp; - if (fabs(fabs(Up) - 1.) < EPS) { - lp.lam = 0.; - lp.phi = Up < 0. ? -M_HALFPI : M_HALFPI; - } else { - lp.phi = Q->E / sqrt((1. + Up) / (1. - Up)); - if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - lp.lam = - Q->rB * atan2((Sp * Q->cosgam - - Vp * Q->singam), cos(Q->BrA * u)); - } - return lp; -} - - -PJ *PROJECTION(omerc) { - double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0, - gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0; - int alp, gam, no_off = 0; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->no_rot = pj_param(P->ctx, P->params, "bno_rot").i; - if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0) - alpha_c = pj_param(P->ctx, P->params, "ralpha").f; - if ((gam = pj_param(P->ctx, P->params, "tgamma").i) != 0) - gamma = pj_param(P->ctx, P->params, "rgamma").f; - if (alp || gam) { - lamc = pj_param(P->ctx, P->params, "rlonc").f; - no_off = - /* For libproj4 compatibility */ - pj_param(P->ctx, P->params, "tno_off").i - /* for backward compatibility */ - || pj_param(P->ctx, P->params, "tno_uoff").i; - if( no_off ) - { - /* Mark the parameter as used, so that the pj_get_def() return them */ - pj_param(P->ctx, P->params, "sno_uoff"); - pj_param(P->ctx, P->params, "sno_off"); - } - } else { - lam1 = pj_param(P->ctx, P->params, "rlon_1").f; - phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - lam2 = pj_param(P->ctx, P->params, "rlon_2").f; - phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - if (fabs(phi1 - phi2) <= TOL || - (con = fabs(phi1)) <= TOL || - fabs(con - M_HALFPI) <= TOL || - fabs(fabs(P->phi0) - M_HALFPI) <= TOL || - fabs(fabs(phi2) - M_HALFPI) <= TOL) - return pj_default_destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90); - } - com = sqrt(P->one_es); - if (fabs(P->phi0) > EPS) { - sinph0 = sin(P->phi0); - cosph0 = cos(P->phi0); - con = 1. - P->es * sinph0 * sinph0; - Q->B = cosph0 * cosph0; - Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es); - Q->A = Q->B * P->k0 * com / con; - D = Q->B * com / (cosph0 * sqrt(con)); - if ((F = D * D - 1.) <= 0.) - F = 0.; - else { - F = sqrt(F); - if (P->phi0 < 0.) - F = -F; - } - Q->E = F += D; - Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B); - } else { - Q->B = 1. / com; - Q->A = P->k0; - Q->E = D = F = 1.; - } - if (alp || gam) { - if (alp) { - gamma0 = aasin(P->ctx, sin(alpha_c) / D); - if (!gam) - gamma = alpha_c; - } else - alpha_c = aasin(P->ctx, D*sin(gamma0 = gamma)); - P->lam0 = lamc - aasin(P->ctx, .5 * (F - 1. / F) * - tan(gamma0)) / Q->B; - } else { - H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B); - L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B); - F = Q->E / H; - p = (L - H) / (L + H); - J = Q->E * Q->E; - J = (J - L * H) / (J + L * H); - if ((con = lam1 - lam2) < -M_PI) - lam2 -= M_TWOPI; - else if (con > M_PI) - lam2 += M_TWOPI; - P->lam0 = adjlon(.5 * (lam1 + lam2) - atan( - J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B); - gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) / - (F - 1. / F)); - gamma = alpha_c = aasin(P->ctx, D * sin(gamma0)); - } - Q->singam = sin(gamma0); - Q->cosgam = cos(gamma0); - Q->sinrot = sin(gamma); - Q->cosrot = cos(gamma); - Q->BrA = 1. / (Q->ArB = Q->A * (Q->rB = 1. / Q->B)); - Q->AB = Q->A * Q->B; - if (no_off) - Q->u_0 = 0; - else { - Q->u_0 = fabs(Q->ArB * atan(sqrt(D * D - 1.) / cos(alpha_c))); - if (P->phi0 < 0.) - Q->u_0 = - Q->u_0; - } - F = 0.5 * gamma0; - Q->v_pole_n = Q->ArB * log(tan(M_FORTPI - F)); - Q->v_pole_s = Q->ArB * log(tan(M_FORTPI + F)); - P->inv = e_inverse; - P->fwd = e_forward; - - return P; -} diff --git a/src/PJ_omerc.cpp b/src/PJ_omerc.cpp new file mode 100644 index 00000000..27b65cc7 --- /dev/null +++ b/src/PJ_omerc.cpp @@ -0,0 +1,227 @@ +/* +** Copyright (c) 2003, 2006 Gerald I. Evenden +*/ +/* +** 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. +*/ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(omerc, "Oblique Mercator") + "\n\tCyl, Sph&Ell no_rot\n\t" + "alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2="; + +struct pj_opaque { + double A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot; + double v_pole_n, v_pole_s, u_0; + int no_rot; +}; + +#define TOL 1.e-7 +#define EPS 1.e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double S, T, U, V, W, temp, u, v; + + if (fabs(fabs(lp.phi) - M_HALFPI) > EPS) { + W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B); + temp = 1. / W; + S = .5 * (W - temp); + T = .5 * (W + temp); + V = sin(Q->B * lp.lam); + U = (S * Q->singam - V * Q->cosgam) / T; + if (fabs(fabs(U) - 1.0) < EPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + v = 0.5 * Q->ArB * log((1. - U)/(1. + U)); + temp = cos(Q->B * lp.lam); + if(fabs(temp) < TOL) { + u = Q->A * lp.lam; + } else { + u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp); + } + } else { + v = lp.phi > 0 ? Q->v_pole_n : Q->v_pole_s; + u = Q->ArB * lp.phi; + } + if (Q->no_rot) { + xy.x = u; + xy.y = v; + } else { + u -= Q->u_0; + xy.x = v * Q->cosrot + u * Q->sinrot; + xy.y = u * Q->cosrot - v * Q->sinrot; + } + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double u, v, Qp, Sp, Tp, Vp, Up; + + if (Q->no_rot) { + v = xy.y; + u = xy.x; + } else { + v = xy.x * Q->cosrot - xy.y * Q->sinrot; + u = xy.y * Q->cosrot + xy.x * Q->sinrot + Q->u_0; + } + Qp = exp(- Q->BrA * v); + Sp = .5 * (Qp - 1. / Qp); + Tp = .5 * (Qp + 1. / Qp); + Vp = sin(Q->BrA * u); + Up = (Vp * Q->cosgam + Sp * Q->singam) / Tp; + if (fabs(fabs(Up) - 1.) < EPS) { + lp.lam = 0.; + lp.phi = Up < 0. ? -M_HALFPI : M_HALFPI; + } else { + lp.phi = Q->E / sqrt((1. + Up) / (1. - Up)); + if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + lp.lam = - Q->rB * atan2((Sp * Q->cosgam - + Vp * Q->singam), cos(Q->BrA * u)); + } + return lp; +} + + +PJ *PROJECTION(omerc) { + double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0, + gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0; + int alp, gam, no_off = 0; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->no_rot = pj_param(P->ctx, P->params, "bno_rot").i; + if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0) + alpha_c = pj_param(P->ctx, P->params, "ralpha").f; + if ((gam = pj_param(P->ctx, P->params, "tgamma").i) != 0) + gamma = pj_param(P->ctx, P->params, "rgamma").f; + if (alp || gam) { + lamc = pj_param(P->ctx, P->params, "rlonc").f; + no_off = + /* For libproj4 compatibility */ + pj_param(P->ctx, P->params, "tno_off").i + /* for backward compatibility */ + || pj_param(P->ctx, P->params, "tno_uoff").i; + if( no_off ) + { + /* Mark the parameter as used, so that the pj_get_def() return them */ + pj_param(P->ctx, P->params, "sno_uoff"); + pj_param(P->ctx, P->params, "sno_off"); + } + } else { + lam1 = pj_param(P->ctx, P->params, "rlon_1").f; + phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + lam2 = pj_param(P->ctx, P->params, "rlon_2").f; + phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + if (fabs(phi1 - phi2) <= TOL || + (con = fabs(phi1)) <= TOL || + fabs(con - M_HALFPI) <= TOL || + fabs(fabs(P->phi0) - M_HALFPI) <= TOL || + fabs(fabs(phi2) - M_HALFPI) <= TOL) + return pj_default_destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90); + } + com = sqrt(P->one_es); + if (fabs(P->phi0) > EPS) { + sinph0 = sin(P->phi0); + cosph0 = cos(P->phi0); + con = 1. - P->es * sinph0 * sinph0; + Q->B = cosph0 * cosph0; + Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es); + Q->A = Q->B * P->k0 * com / con; + D = Q->B * com / (cosph0 * sqrt(con)); + if ((F = D * D - 1.) <= 0.) + F = 0.; + else { + F = sqrt(F); + if (P->phi0 < 0.) + F = -F; + } + Q->E = F += D; + Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B); + } else { + Q->B = 1. / com; + Q->A = P->k0; + Q->E = D = F = 1.; + } + if (alp || gam) { + if (alp) { + gamma0 = aasin(P->ctx, sin(alpha_c) / D); + if (!gam) + gamma = alpha_c; + } else + alpha_c = aasin(P->ctx, D*sin(gamma0 = gamma)); + P->lam0 = lamc - aasin(P->ctx, .5 * (F - 1. / F) * + tan(gamma0)) / Q->B; + } else { + H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B); + L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B); + F = Q->E / H; + p = (L - H) / (L + H); + J = Q->E * Q->E; + J = (J - L * H) / (J + L * H); + if ((con = lam1 - lam2) < -M_PI) + lam2 -= M_TWOPI; + else if (con > M_PI) + lam2 += M_TWOPI; + P->lam0 = adjlon(.5 * (lam1 + lam2) - atan( + J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B); + gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) / + (F - 1. / F)); + gamma = alpha_c = aasin(P->ctx, D * sin(gamma0)); + } + Q->singam = sin(gamma0); + Q->cosgam = cos(gamma0); + Q->sinrot = sin(gamma); + Q->cosrot = cos(gamma); + Q->BrA = 1. / (Q->ArB = Q->A * (Q->rB = 1. / Q->B)); + Q->AB = Q->A * Q->B; + if (no_off) + Q->u_0 = 0; + else { + Q->u_0 = fabs(Q->ArB * atan(sqrt(D * D - 1.) / cos(alpha_c))); + if (P->phi0 < 0.) + Q->u_0 = - Q->u_0; + } + F = 0.5 * gamma0; + Q->v_pole_n = Q->ArB * log(tan(M_FORTPI - F)); + Q->v_pole_s = Q->ArB * log(tan(M_FORTPI + F)); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} diff --git a/src/PJ_ortho.c b/src/PJ_ortho.c deleted file mode 100644 index d442aa8a..00000000 --- a/src/PJ_ortho.c +++ /dev/null @@ -1,139 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -PROJ_HEAD(ortho, "Orthographic") "\n\tAzi, Sph"; - -enum Mode { - N_POLE = 0, - S_POLE = 1, - EQUIT = 2, - OBLIQ = 3 -}; - -struct pj_opaque { - double sinph0; - double cosph0; - enum Mode mode; -}; - -#define EPS10 1.e-10 - -static XY forward_error(PJ *P, LP lp, XY xy) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - proj_log_trace(P, "Coordinate (%.3f, %.3f) is on the unprojected hemisphere", - proj_todeg(lp.lam), proj_todeg(lp.phi)); - return xy; -} - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy; - struct pj_opaque *Q = P->opaque; - double coslam, cosphi, sinphi; - - xy.x = HUGE_VAL; xy.y = HUGE_VAL; - - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (Q->mode) { - case EQUIT: - if (cosphi * coslam < - EPS10) - return forward_error(P, lp, xy); - xy.y = sin(lp.phi); - break; - case OBLIQ: - if (Q->sinph0 * (sinphi = sin(lp.phi)) + Q->cosph0 * cosphi * coslam < - EPS10) - return forward_error(P, lp, xy); - xy.y = Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = - coslam; - /*-fallthrough*/ - case S_POLE: - if (fabs(lp.phi - P->phi0) - EPS10 > M_HALFPI) - return forward_error(P, lp, xy); - xy.y = cosphi * coslam; - break; - } - xy.x = cosphi * sin(lp.lam); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp; - struct pj_opaque *Q = P->opaque; - double rh, cosc, sinc; - - lp.lam = HUGE_VAL; lp.phi = HUGE_VAL; - - if ((sinc = (rh = hypot(xy.x, xy.y))) > 1.) { - if ((sinc - 1.) > EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - proj_log_trace(P, "Point (%.3f, %.3f) is outside the projection boundary"); - return lp; - } - sinc = 1.; - } - cosc = sqrt(1. - sinc * sinc); /* in this range OK */ - if (fabs(rh) <= EPS10) { - lp.phi = P->phi0; - lp.lam = 0.0; - } else { - switch (Q->mode) { - case N_POLE: - xy.y = -xy.y; - lp.phi = acos(sinc); - break; - case S_POLE: - lp.phi = - acos(sinc); - break; - case EQUIT: - lp.phi = xy.y * sinc / rh; - xy.x *= sinc; - xy.y = cosc * rh; - goto sinchk; - case OBLIQ: - lp.phi = cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 /rh; - xy.y = (cosc - Q->sinph0 * lp.phi) * rh; - xy.x *= sinc * Q->cosph0; - sinchk: - if (fabs(lp.phi) >= 1.) - lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; - else - lp.phi = asin(lp.phi); - break; - } - lp.lam = (xy.y == 0. && (Q->mode == OBLIQ || Q->mode == EQUIT)) - ? (xy.x == 0. ? 0. : xy.x < 0. ? -M_HALFPI : M_HALFPI) - : atan2(xy.x, xy.y); - } - return lp; -} - - - -PJ *PROJECTION(ortho) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - if (fabs(fabs(P->phi0) - M_HALFPI) <= EPS10) - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(P->phi0) > EPS10) { - Q->mode = OBLIQ; - Q->sinph0 = sin(P->phi0); - Q->cosph0 = cos(P->phi0); - } else - Q->mode = EQUIT; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} - diff --git a/src/PJ_ortho.cpp b/src/PJ_ortho.cpp new file mode 100644 index 00000000..0e3641c0 --- /dev/null +++ b/src/PJ_ortho.cpp @@ -0,0 +1,139 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +PROJ_HEAD(ortho, "Orthographic") "\n\tAzi, Sph"; + +enum Mode { + N_POLE = 0, + S_POLE = 1, + EQUIT = 2, + OBLIQ = 3 +}; + +struct pj_opaque { + double sinph0; + double cosph0; + enum Mode mode; +}; + +#define EPS10 1.e-10 + +static XY forward_error(PJ *P, LP lp, XY xy) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + proj_log_trace(P, "Coordinate (%.3f, %.3f) is on the unprojected hemisphere", + proj_todeg(lp.lam), proj_todeg(lp.phi)); + return xy; +} + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, cosphi, sinphi; + + xy.x = HUGE_VAL; xy.y = HUGE_VAL; + + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + if (cosphi * coslam < - EPS10) + return forward_error(P, lp, xy); + xy.y = sin(lp.phi); + break; + case OBLIQ: + if (Q->sinph0 * (sinphi = sin(lp.phi)) + Q->cosph0 * cosphi * coslam < - EPS10) + return forward_error(P, lp, xy); + xy.y = Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; + break; + case N_POLE: + coslam = - coslam; + /*-fallthrough*/ + case S_POLE: + if (fabs(lp.phi - P->phi0) - EPS10 > M_HALFPI) + return forward_error(P, lp, xy); + xy.y = cosphi * coslam; + break; + } + xy.x = cosphi * sin(lp.lam); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp; + struct pj_opaque *Q = static_cast(P->opaque); + double rh, cosc, sinc; + + lp.lam = HUGE_VAL; lp.phi = HUGE_VAL; + + if ((sinc = (rh = hypot(xy.x, xy.y))) > 1.) { + if ((sinc - 1.) > EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + proj_log_trace(P, "Point (%.3f, %.3f) is outside the projection boundary"); + return lp; + } + sinc = 1.; + } + cosc = sqrt(1. - sinc * sinc); /* in this range OK */ + if (fabs(rh) <= EPS10) { + lp.phi = P->phi0; + lp.lam = 0.0; + } else { + switch (Q->mode) { + case N_POLE: + xy.y = -xy.y; + lp.phi = acos(sinc); + break; + case S_POLE: + lp.phi = - acos(sinc); + break; + case EQUIT: + lp.phi = xy.y * sinc / rh; + xy.x *= sinc; + xy.y = cosc * rh; + goto sinchk; + case OBLIQ: + lp.phi = cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 /rh; + xy.y = (cosc - Q->sinph0 * lp.phi) * rh; + xy.x *= sinc * Q->cosph0; + sinchk: + if (fabs(lp.phi) >= 1.) + lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI; + else + lp.phi = asin(lp.phi); + break; + } + lp.lam = (xy.y == 0. && (Q->mode == OBLIQ || Q->mode == EQUIT)) + ? (xy.x == 0. ? 0. : xy.x < 0. ? -M_HALFPI : M_HALFPI) + : atan2(xy.x, xy.y); + } + return lp; +} + + + +PJ *PROJECTION(ortho) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + if (fabs(fabs(P->phi0) - M_HALFPI) <= EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else if (fabs(P->phi0) > EPS10) { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } else + Q->mode = EQUIT; + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} + diff --git a/src/PJ_patterson.c b/src/PJ_patterson.c deleted file mode 100644 index 0d19414e..00000000 --- a/src/PJ_patterson.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (c) 2014 Bojan Savric - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * The Patterson Cylindrical projection was designed by Tom Patterson, US National - * Park Service, in 2014, using Flex Projector. The polynomial equations for the - * projection were developed by Bojan Savric, Oregon State University, in - * collaboration with Tom Patterson and Bernhard Jenny, Oregon State University. - * - * Java reference algorithm implemented by Bojan Savric in Java Map Projection - * Library (a Java port of PROJ.4) in the file PattersonProjection.java. - * - * References: - * Java Map Projection Library - * https://github.com/OSUCartography/JMapProjLib - * - * Patterson Cylindrical Projection - * http://shadedrelief.com/patterson/ - * - * Patterson, T., Savric, B., and Jenny, B. (2015). Cartographic Perspectives - * (No.78). Describes the projection design and characteristics, and - * developing the equations. doi:10.14714/CP78.1270 - * https://doi.org/10.14714/CP78.1270 - * - * Port to PROJ.4 by Micah Cochran, 26 March 2016 - */ - -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(patterson, "Patterson Cylindrical") "\n\tCyl"; - -#define K1 1.0148 -#define K2 0.23185 -#define K3 -0.14499 -#define K4 0.02406 -#define C1 K1 -#define C2 (5.0 * K2) -#define C3 (7.0 * K3) -#define C4 (9.0 * K4) -#define EPS11 1.0e-11 -#define MAX_Y 1.790857183 -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double phi2; - (void) P; - - phi2 = lp.phi * lp.phi; - xy.x = lp.lam; - xy.y = lp.phi * (K1 + phi2 * phi2 * (K2 + phi2 * (K3 + K4 * phi2))); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double yc, tol, y2, f, fder; - int i; - (void) P; - - yc = xy.y; - - /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } - - for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ - y2 = yc * yc; - f = (yc * (K1 + y2 * y2 * (K2 + y2 * (K3 + K4 * y2)))) - xy.y; - fder = C1 + y2 * y2 * (C2 + y2 * (C3 + C4 * y2)); - yc -= tol = f / fder; - if (fabs(tol) < EPS11) { - break; - } - } - if( i == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - lp.phi = yc; - - /* longitude */ - lp.lam = xy.x; - - return lp; -} - - -PJ *PROJECTION(patterson) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_patterson.cpp b/src/PJ_patterson.cpp new file mode 100644 index 00000000..0d19414e --- /dev/null +++ b/src/PJ_patterson.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2014 Bojan Savric + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The Patterson Cylindrical projection was designed by Tom Patterson, US National + * Park Service, in 2014, using Flex Projector. The polynomial equations for the + * projection were developed by Bojan Savric, Oregon State University, in + * collaboration with Tom Patterson and Bernhard Jenny, Oregon State University. + * + * Java reference algorithm implemented by Bojan Savric in Java Map Projection + * Library (a Java port of PROJ.4) in the file PattersonProjection.java. + * + * References: + * Java Map Projection Library + * https://github.com/OSUCartography/JMapProjLib + * + * Patterson Cylindrical Projection + * http://shadedrelief.com/patterson/ + * + * Patterson, T., Savric, B., and Jenny, B. (2015). Cartographic Perspectives + * (No.78). Describes the projection design and characteristics, and + * developing the equations. doi:10.14714/CP78.1270 + * https://doi.org/10.14714/CP78.1270 + * + * Port to PROJ.4 by Micah Cochran, 26 March 2016 + */ + +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(patterson, "Patterson Cylindrical") "\n\tCyl"; + +#define K1 1.0148 +#define K2 0.23185 +#define K3 -0.14499 +#define K4 0.02406 +#define C1 K1 +#define C2 (5.0 * K2) +#define C3 (7.0 * K3) +#define C4 (9.0 * K4) +#define EPS11 1.0e-11 +#define MAX_Y 1.790857183 +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2; + (void) P; + + phi2 = lp.phi * lp.phi; + xy.x = lp.lam; + xy.y = lp.phi * (K1 + phi2 * phi2 * (K2 + phi2 * (K3 + K4 * phi2))); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, f, fder; + int i; + (void) P; + + yc = xy.y; + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */ + y2 = yc * yc; + f = (yc * (K1 + y2 * y2 * (K2 + y2 * (K3 + K4 * y2)))) - xy.y; + fder = C1 + y2 * y2 * (C2 + y2 * (C3 + C4 * y2)); + yc -= tol = f / fder; + if (fabs(tol) < EPS11) { + break; + } + } + if( i == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + lp.phi = yc; + + /* longitude */ + lp.lam = xy.x; + + return lp; +} + + +PJ *PROJECTION(patterson) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_pipeline.c b/src/PJ_pipeline.c deleted file mode 100644 index 618d4688..00000000 --- a/src/PJ_pipeline.c +++ /dev/null @@ -1,501 +0,0 @@ -/******************************************************************************* - - Transformation pipeline manager - - Thomas Knudsen, 2016-05-20/2016-11-20 - -******************************************************************************** - - Geodetic transformations are typically organized in a number of - steps. For example, a datum shift could be carried out through - these steps: - - 1. Convert (latitude, longitude, ellipsoidal height) to - 3D geocentric cartesian coordinates (X, Y, Z) - 2. Transform the (X, Y, Z) coordinates to the new datum, using a - 7 parameter Helmert transformation. - 3. Convert (X, Y, Z) back to (latitude, longitude, ellipsoidal height) - - If the height system used is orthometric, rather than ellipsoidal, - another step is needed at each end of the process: - - 1. Add the local geoid undulation (N) to the orthometric height - to obtain the ellipsoidal (i.e. geometric) height. - 2. Convert (latitude, longitude, ellipsoidal height) to - 3D geocentric cartesian coordinates (X, Y, Z) - 3. Transform the (X, Y, Z) coordinates to the new datum, using a - 7 parameter Helmert transformation. - 4. Convert (X, Y, Z) back to (latitude, longitude, ellipsoidal height) - 5. Subtract the local geoid undulation (N) from the ellipsoidal height - to obtain the orthometric height. - - Additional steps can be added for e.g. change of vertical datum, so the - list can grow fairly long. None of the steps are, however, particularly - complex, and data flow is strictly from top to bottom. - - Hence, in principle, the first example above could be implemented using - Unix pipelines: - - cat my_coordinates | geographic_to_xyz | helmert | xyz_to_geographic > my_transformed_coordinates - - in the grand tradition of Software Tools [1]. - - The proj pipeline driver implements a similar concept: Stringing together - a number of steps, feeding the output of one step to the input of the next. - - It is a very powerful concept, that increases the range of relevance of the - proj.4 system substantially. It is, however, not a particularly intrusive - addition to the PROJ.4 code base: The implementation is by and large completed - by adding an extra projection called "pipeline" (i.e. this file), which - handles all business, and a small amount of added functionality in the - pj_init code, implementing support for multilevel, embedded pipelines. - - Syntactically, the pipeline system introduces the "+step" keyword (which - indicates the start of each transformation step), and reintroduces the +inv - keyword (indicating that a given transformation step should run in reverse, i.e. - forward, when the pipeline is executed in inverse direction, and vice versa). - - Hence, the first transformation example above, can be implemented as: - - +proj=pipeline +step proj=cart +step proj=helmert +step proj=cart +inv - - Where indicate the Helmert arguments: 3 translations (+x=..., +y=..., - +z=...), 3 rotations (+rx=..., +ry=..., +rz=...) and a scale factor (+s=...). - Following geodetic conventions, the rotations are given in arcseconds, - and the scale factor is given as parts-per-million. - - [1] B. W. Kernighan & P. J. Plauger: Software tools. - Reading, Massachusetts, Addison-Wesley, 1976, 338 pp. - -******************************************************************************** - -Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 - -******************************************************************************** -* Copyright (c) 2016, 2017, 2018 Thomas Knudsen / SDFE -* -* 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. -* -********************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "geodesic.h" -#include "proj.h" -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(pipeline, "Transformation pipeline manager"); - -/* Projection specific elements for the PJ object */ -struct pj_opaque { - int steps; - char **argv; - char **current_argv; - PJ **pipeline; -}; - - - -static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P); -static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P); -static XYZ pipeline_forward_3d (LPZ lpz, PJ *P); -static LPZ pipeline_reverse_3d (XYZ xyz, PJ *P); -static XY pipeline_forward (LP lp, PJ *P); -static LP pipeline_reverse (XY xy, PJ *P); - - - - -static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { - int i, first_step, last_step; - - first_step = 1; - last_step = P->opaque->steps + 1; - - for (i = first_step; i != last_step; i++) - point = proj_trans (P->opaque->pipeline[i], 1, point); - - return point; -} - - -static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { - int i, first_step, last_step; - - first_step = P->opaque->steps; - last_step = 0; - - for (i = first_step; i != last_step; i--) - point = proj_trans (P->opaque->pipeline[i], -1, point); - - return point; -} - - - - -static XYZ pipeline_forward_3d (LPZ lpz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - int i; - point.lpz = lpz; - - for (i = 1; i <= P->opaque->steps; i++) - point = pj_approx_3D_trans (P->opaque->pipeline[i], 1, point); - - return point.xyz; -} - - -static LPZ pipeline_reverse_3d (XYZ xyz, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - int i; - point.xyz = xyz; - - for (i = P->opaque->steps; i > 0 ; i--) - point = pj_approx_3D_trans (P->opaque->pipeline[i], -1, point); - - return point.lpz; -} - - - - -static XY pipeline_forward (LP lp, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - int i; - point.lp = lp; - - for (i = 1; i <= P->opaque->steps; i++) - point = pj_approx_2D_trans (P->opaque->pipeline[i], 1, point); - - return point.xy; -} - - -static LP pipeline_reverse (XY xy, PJ *P) { - PJ_COORD point = {{0,0,0,0}}; - int i; - point.xy = xy; - for (i = P->opaque->steps; i > 0 ; i--) - point = pj_approx_2D_trans (P->opaque->pipeline[i], -1, point); - - return point.lp; -} - - - - -static void *destructor (PJ *P, int errlev) { - int i; - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - /* Deallocate each pipeine step, then pipeline array */ - if (0!=P->opaque->pipeline) - for (i = 0; i < P->opaque->steps; i++) - proj_destroy (P->opaque->pipeline[i+1]); - pj_dealloc (P->opaque->pipeline); - - pj_dealloc (P->opaque->argv); - pj_dealloc (P->opaque->current_argv); - - return pj_default_destructor(P, errlev); -} - - -static PJ *pj_create_pipeline (PJ *P, size_t steps) { - - /* Room for the pipeline: An array of PJ * with room for sentinels at both ends */ - P->opaque->pipeline = pj_calloc (steps + 2, sizeof(PJ *)); - if (0==P->opaque->pipeline) - return 0; - - P->opaque->steps = (int)steps; - - return P; -} - - - - -/* count the number of args in pipeline definition, and mark all args as used */ -static size_t argc_params (paralist *params) { - size_t argc = 0; - for (; params != 0; params = params->next) { - argc++; - params->used = 1; - } - return ++argc; /* one extra for the sentinel */ -} - -/* Sentinel for argument list */ -static char *argv_sentinel = "step"; - -/* turn paralist into argc/argv style argument list */ -static char **argv_params (paralist *params, size_t argc) { - char **argv; - size_t i = 0; - argv = pj_calloc (argc, sizeof (char *)); - if (0==argv) - return 0; - for (; params != 0; params = params->next) - argv[i++] = params->param; - argv[i++] = argv_sentinel; - return argv; -} - - - - -/* Being the special operator that the pipeline is, we have to handle the */ -/* ellipsoid differently than usual. In general, the pipeline operation does */ -/* not need an ellipsoid, but in some cases it is beneficial nonetheless. */ -/* Unfortunately we can't use the normal ellipsoid setter in pj_init, since */ -/* it adds a +ellps parameter to the global args if nothing else is specified*/ -/* This is problematic since that ellipsoid spec is then passed on to the */ -/* pipeline children. This is rarely what we want, so here we implement our */ -/* own logic instead. If an ellipsoid is set in the global args, it is used */ -/* as the pipeline ellipsoid. Otherwise we use WGS84 parameters as default. */ -/* At last we calculate the rest of the ellipsoid parameters and */ -/* re-initialize P->geod. */ -static void set_ellipsoid(PJ *P) { - paralist *cur, *attachment; - int err = proj_errno_reset (P); - - /* Break the linked list after the global args */ - attachment = 0; - for (cur = P->params; cur != 0; cur = cur->next) - /* cur->next will always be non 0 given argv_sentinel presence, */ - /* but this is far from being obvious for a static analyzer */ - if (cur->next != 0 && strcmp(argv_sentinel, cur->next->param) == 0) { - attachment = cur->next; - cur->next = 0; - break; - } - - /* Check if there's any ellipsoid specification in the global params. */ - /* If not, use WGS84 as default */ - if (0 != pj_ellipsoid (P)) { - P->a = 6378137.0; - P->es = .00669438002290341575; - - /* reset an "unerror": In this special use case, the errno is */ - /* not an error signal, but just a reply from pj_ellipsoid, */ - /* telling us that "No - there was no ellipsoid definition in */ - /* the PJ you provided". */ - proj_errno_reset (P); - } - P->a_orig = P->a; - P->es_orig = P->es; - - pj_calc_ellipsoid_params (P, P->a, P->es); - - geod_init(P->geod, P->a, (1 - sqrt (1 - P->es))); - - /* Re-attach the dangling list */ - /* Note: cur will always be non 0 given argv_sentinel presence, */ - /* but this is far from being obvious for a static analyzer */ - if( cur != 0 ) - cur->next = attachment; - proj_errno_restore (P, err); -} - - - - -PJ *OPERATION(pipeline,0) { - int i, nsteps = 0, argc; - int i_pipeline = -1, i_first_step = -1, i_current_step; - char **argv, **current_argv; - - P->fwd4d = pipeline_forward_4d; - P->inv4d = pipeline_reverse_4d; - P->fwd3d = pipeline_forward_3d; - P->inv3d = pipeline_reverse_3d; - P->fwd = pipeline_forward; - P->inv = pipeline_reverse; - P->destructor = destructor; - P->is_pipeline = 1; - - /* Currently, the pipeline driver is a raw bit mover, enabling other operations */ - /* to collaborate efficiently. All prep/fin stuff is done at the step levels. */ - P->skip_fwd_prepare = 1; - P->skip_fwd_finalize = 1; - P->skip_inv_prepare = 1; - P->skip_inv_finalize = 1; - - - P->opaque = pj_calloc (1, sizeof(struct pj_opaque)); - if (0==P->opaque) - return destructor(P, ENOMEM); - - argc = (int)argc_params (P->params); - P->opaque->argv = argv = argv_params (P->params, argc); - if (0==argv) - return destructor (P, ENOMEM); - - P->opaque->current_argv = current_argv = pj_calloc (argc, sizeof (char *)); - if (0==current_argv) - return destructor (P, ENOMEM); - - /* Do some syntactical sanity checking */ - for (i = 0; i < argc; i++) { - if (0==strcmp (argv_sentinel, argv[i])) { - if (-1==i_pipeline) { - proj_log_error (P, "Pipeline: +step before +proj=pipeline"); - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); - } - if (0==nsteps) - i_first_step = i; - nsteps++; - continue; - } - - if (0==strcmp ("proj=pipeline", argv[i])) { - if (-1 != i_pipeline) { - proj_log_error (P, "Pipeline: Nesting only allowed when child pipelines are wrapped in '+init's"); - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: nested pipelines */ - } - i_pipeline = i; - } - } - nsteps--; /* Last instance of +step is just a sentinel */ - P->opaque->steps = nsteps; - - if (-1==i_pipeline) - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ - - if (0==nsteps) - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ - - /* Make room for the pipeline and execution indicators */ - if (0==pj_create_pipeline (P, nsteps)) - return destructor (P, ENOMEM); - - set_ellipsoid(P); - - /* Now loop over all steps, building a new set of arguments for each init */ - i_current_step = i_first_step; - for (i = 0; i < nsteps; i++) { - int j; - int current_argc = 0; - int err; - PJ *next_step = 0; - - /* Build a set of setup args for the current step */ - proj_log_trace (P, "Pipeline: Building arg list for step no. %d", i); - - /* First add the step specific args */ - for (j = i_current_step + 1; 0 != strcmp ("step", argv[j]); j++) - current_argv[current_argc++] = argv[j]; - - i_current_step = j; - - /* Then add the global args */ - for (j = i_pipeline + 1; 0 != strcmp ("step", argv[j]); j++) - current_argv[current_argc++] = argv[j]; - - proj_log_trace (P, "Pipeline: init - %s, %d", current_argv[0], current_argc); - for (j = 1; j < current_argc; j++) - proj_log_trace (P, " %s", current_argv[j]); - - err = proj_errno_reset (P); - - next_step = proj_create_argv (P->ctx, current_argc, current_argv); - proj_log_trace (P, "Pipeline: Step %d (%s) at %p", i, current_argv[0], next_step); - - if (0==next_step) { - /* The step init failed, but possibly without setting errno. If so, we say "malformed" */ - int err_to_report = proj_errno(P); - if (0==err_to_report) - err_to_report = PJD_ERR_MALFORMED_PIPELINE; - proj_log_error (P, "Pipeline: Bad step definition: %s (%s)", current_argv[0], pj_strerrno (err_to_report)); - return destructor (P, err_to_report); /* ERROR: bad pipeline def */ - } - - proj_errno_restore (P, err); - - /* Is this step inverted? */ - for (j = 0; j < current_argc; j++) - if (0==strcmp("inv", current_argv[j])) { - /* if +inv exists in both global and local args the forward operation should be used */ - next_step->inverted = next_step->inverted == 0 ? 1 : 0; - } - - P->opaque->pipeline[i+1] = next_step; - - proj_log_trace (P, "Pipeline at [%p]: step at [%p] (%s) done", P, next_step, current_argv[0]); - } - - /* Require a forward path through the pipeline */ - for (i = 1; i <= nsteps; i++) { - PJ *Q = P->opaque->pipeline[i]; - if ( ( Q->inverted && (Q->inv || Q->inv3d || Q->fwd4d) ) || - (!Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) ) { - continue; - } else { - proj_log_error (P, "Pipeline: A forward operation couldn't be constructed"); - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); - } - } - - /* determine if an inverse operation is possible */ - for (i = 1; i <= nsteps; i++) { - PJ *Q = P->opaque->pipeline[i]; - if ( pj_has_inverse(Q) ) { - continue; - } else { - P->inv = 0; - P->inv3d = 0; - P->inv4d = 0; - break; - } - } - - /* Check that output units from step i are compatible with expected units in step i+1 */ - for (i = 1; i < nsteps; i++) { - enum pj_io_units unit_returned = pj_right (P->opaque->pipeline[i]); - enum pj_io_units unit_expected = pj_left (P->opaque->pipeline[i+1]); - - if ( unit_returned == PJ_IO_UNITS_WHATEVER || unit_expected == PJ_IO_UNITS_WHATEVER ) - continue; - if ( unit_returned != unit_expected ) { - proj_log_error (P, "Pipeline: Mismatched units between step %d and %d", i, i+1); - return destructor (P, PJD_ERR_MALFORMED_PIPELINE); - } - } - - proj_log_trace (P, "Pipeline: %d steps built. Determining i/o characteristics", nsteps); - - /* Determine forward input (= reverse output) data type */ - P->left = pj_left (P->opaque->pipeline[1]); - - /* Now, correspondingly determine forward output (= reverse input) data type */ - P->right = pj_right (P->opaque->pipeline[nsteps]); - return P; -} diff --git a/src/PJ_pipeline.cpp b/src/PJ_pipeline.cpp new file mode 100644 index 00000000..c20454df --- /dev/null +++ b/src/PJ_pipeline.cpp @@ -0,0 +1,501 @@ +/******************************************************************************* + + Transformation pipeline manager + + Thomas Knudsen, 2016-05-20/2016-11-20 + +******************************************************************************** + + Geodetic transformations are typically organized in a number of + steps. For example, a datum shift could be carried out through + these steps: + + 1. Convert (latitude, longitude, ellipsoidal height) to + 3D geocentric cartesian coordinates (X, Y, Z) + 2. Transform the (X, Y, Z) coordinates to the new datum, using a + 7 parameter Helmert transformation. + 3. Convert (X, Y, Z) back to (latitude, longitude, ellipsoidal height) + + If the height system used is orthometric, rather than ellipsoidal, + another step is needed at each end of the process: + + 1. Add the local geoid undulation (N) to the orthometric height + to obtain the ellipsoidal (i.e. geometric) height. + 2. Convert (latitude, longitude, ellipsoidal height) to + 3D geocentric cartesian coordinates (X, Y, Z) + 3. Transform the (X, Y, Z) coordinates to the new datum, using a + 7 parameter Helmert transformation. + 4. Convert (X, Y, Z) back to (latitude, longitude, ellipsoidal height) + 5. Subtract the local geoid undulation (N) from the ellipsoidal height + to obtain the orthometric height. + + Additional steps can be added for e.g. change of vertical datum, so the + list can grow fairly long. None of the steps are, however, particularly + complex, and data flow is strictly from top to bottom. + + Hence, in principle, the first example above could be implemented using + Unix pipelines: + + cat my_coordinates | geographic_to_xyz | helmert | xyz_to_geographic > my_transformed_coordinates + + in the grand tradition of Software Tools [1]. + + The proj pipeline driver implements a similar concept: Stringing together + a number of steps, feeding the output of one step to the input of the next. + + It is a very powerful concept, that increases the range of relevance of the + proj.4 system substantially. It is, however, not a particularly intrusive + addition to the PROJ.4 code base: The implementation is by and large completed + by adding an extra projection called "pipeline" (i.e. this file), which + handles all business, and a small amount of added functionality in the + pj_init code, implementing support for multilevel, embedded pipelines. + + Syntactically, the pipeline system introduces the "+step" keyword (which + indicates the start of each transformation step), and reintroduces the +inv + keyword (indicating that a given transformation step should run in reverse, i.e. + forward, when the pipeline is executed in inverse direction, and vice versa). + + Hence, the first transformation example above, can be implemented as: + + +proj=pipeline +step proj=cart +step proj=helmert +step proj=cart +inv + + Where indicate the Helmert arguments: 3 translations (+x=..., +y=..., + +z=...), 3 rotations (+rx=..., +ry=..., +rz=...) and a scale factor (+s=...). + Following geodetic conventions, the rotations are given in arcseconds, + and the scale factor is given as parts-per-million. + + [1] B. W. Kernighan & P. J. Plauger: Software tools. + Reading, Massachusetts, Addison-Wesley, 1976, 338 pp. + +******************************************************************************** + +Thomas Knudsen, thokn@sdfe.dk, 2016-05-20 + +******************************************************************************** +* Copyright (c) 2016, 2017, 2018 Thomas Knudsen / SDFE +* +* 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. +* +********************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "geodesic.h" +#include "proj.h" +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(pipeline, "Transformation pipeline manager"); + +/* Projection specific elements for the PJ object */ +struct pj_opaque { + int steps; + char **argv; + char **current_argv; + PJ **pipeline; +}; + + + +static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P); +static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P); +static XYZ pipeline_forward_3d (LPZ lpz, PJ *P); +static LPZ pipeline_reverse_3d (XYZ xyz, PJ *P); +static XY pipeline_forward (LP lp, PJ *P); +static LP pipeline_reverse (XY xy, PJ *P); + + + + +static PJ_COORD pipeline_forward_4d (PJ_COORD point, PJ *P) { + int i, first_step, last_step; + + first_step = 1; + last_step = static_cast(P->opaque)->steps + 1; + + for (i = first_step; i != last_step; i++) + point = proj_trans (static_cast(P->opaque)->pipeline[i], PJ_FWD, point); + + return point; +} + + +static PJ_COORD pipeline_reverse_4d (PJ_COORD point, PJ *P) { + int i, first_step, last_step; + + first_step = static_cast(P->opaque)->steps; + last_step = 0; + + for (i = first_step; i != last_step; i--) + point = proj_trans (static_cast(P->opaque)->pipeline[i], PJ_INV, point); + + return point; +} + + + + +static XYZ pipeline_forward_3d (LPZ lpz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + int i; + point.lpz = lpz; + + for (i = 1; i <= static_cast(P->opaque)->steps; i++) + point = pj_approx_3D_trans (static_cast(P->opaque)->pipeline[i], PJ_FWD, point); + + return point.xyz; +} + + +static LPZ pipeline_reverse_3d (XYZ xyz, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + int i; + point.xyz = xyz; + + for (i = static_cast(P->opaque)->steps; i > 0 ; i--) + point = pj_approx_3D_trans (static_cast(P->opaque)->pipeline[i], PJ_INV, point); + + return point.lpz; +} + + + + +static XY pipeline_forward (LP lp, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + int i; + point.lp = lp; + + for (i = 1; i <= static_cast(P->opaque)->steps; i++) + point = pj_approx_2D_trans (static_cast(P->opaque)->pipeline[i], PJ_FWD, point); + + return point.xy; +} + + +static LP pipeline_reverse (XY xy, PJ *P) { + PJ_COORD point = {{0,0,0,0}}; + int i; + point.xy = xy; + for (i = static_cast(P->opaque)->steps; i > 0 ; i--) + point = pj_approx_2D_trans (static_cast(P->opaque)->pipeline[i], PJ_INV, point); + + return point.lp; +} + + + + +static PJ *destructor (PJ *P, int errlev) { + int i; + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + /* Deallocate each pipeine step, then pipeline array */ + if (0!=static_cast(P->opaque)->pipeline) + for (i = 0; i < static_cast(P->opaque)->steps; i++) + proj_destroy (static_cast(P->opaque)->pipeline[i+1]); + pj_dealloc (static_cast(P->opaque)->pipeline); + + pj_dealloc (static_cast(P->opaque)->argv); + pj_dealloc (static_cast(P->opaque)->current_argv); + + return pj_default_destructor(P, errlev); +} + + +static PJ *pj_create_pipeline (PJ *P, size_t steps) { + + /* Room for the pipeline: An array of PJ * with room for sentinels at both ends */ + static_cast(P->opaque)->pipeline = static_cast(pj_calloc (steps + 2, sizeof(PJ *))); + if (0==static_cast(P->opaque)->pipeline) + return 0; + + static_cast(P->opaque)->steps = (int)steps; + + return P; +} + + + + +/* count the number of args in pipeline definition, and mark all args as used */ +static size_t argc_params (paralist *params) { + size_t argc = 0; + for (; params != 0; params = params->next) { + argc++; + params->used = 1; + } + return ++argc; /* one extra for the sentinel */ +} + +/* Sentinel for argument list */ +static char *argv_sentinel = "step"; + +/* turn paralist into argc/argv style argument list */ +static char **argv_params (paralist *params, size_t argc) { + char **argv; + size_t i = 0; + argv = static_cast(pj_calloc (argc, sizeof (char *))); + if (0==argv) + return 0; + for (; params != 0; params = params->next) + argv[i++] = params->param; + argv[i++] = argv_sentinel; + return argv; +} + + + + +/* Being the special operator that the pipeline is, we have to handle the */ +/* ellipsoid differently than usual. In general, the pipeline operation does */ +/* not need an ellipsoid, but in some cases it is beneficial nonetheless. */ +/* Unfortunately we can't use the normal ellipsoid setter in pj_init, since */ +/* it adds a +ellps parameter to the global args if nothing else is specified*/ +/* This is problematic since that ellipsoid spec is then passed on to the */ +/* pipeline children. This is rarely what we want, so here we implement our */ +/* own logic instead. If an ellipsoid is set in the global args, it is used */ +/* as the pipeline ellipsoid. Otherwise we use WGS84 parameters as default. */ +/* At last we calculate the rest of the ellipsoid parameters and */ +/* re-initialize P->geod. */ +static void set_ellipsoid(PJ *P) { + paralist *cur, *attachment; + int err = proj_errno_reset (P); + + /* Break the linked list after the global args */ + attachment = 0; + for (cur = P->params; cur != 0; cur = cur->next) + /* cur->next will always be non 0 given argv_sentinel presence, */ + /* but this is far from being obvious for a static analyzer */ + if (cur->next != 0 && strcmp(argv_sentinel, cur->next->param) == 0) { + attachment = cur->next; + cur->next = 0; + break; + } + + /* Check if there's any ellipsoid specification in the global params. */ + /* If not, use WGS84 as default */ + if (0 != pj_ellipsoid (P)) { + P->a = 6378137.0; + P->es = .00669438002290341575; + + /* reset an "unerror": In this special use case, the errno is */ + /* not an error signal, but just a reply from pj_ellipsoid, */ + /* telling us that "No - there was no ellipsoid definition in */ + /* the PJ you provided". */ + proj_errno_reset (P); + } + P->a_orig = P->a; + P->es_orig = P->es; + + pj_calc_ellipsoid_params (P, P->a, P->es); + + geod_init(P->geod, P->a, (1 - sqrt (1 - P->es))); + + /* Re-attach the dangling list */ + /* Note: cur will always be non 0 given argv_sentinel presence, */ + /* but this is far from being obvious for a static analyzer */ + if( cur != 0 ) + cur->next = attachment; + proj_errno_restore (P, err); +} + + + + +PJ *OPERATION(pipeline,0) { + int i, nsteps = 0, argc; + int i_pipeline = -1, i_first_step = -1, i_current_step; + char **argv, **current_argv; + + P->fwd4d = pipeline_forward_4d; + P->inv4d = pipeline_reverse_4d; + P->fwd3d = pipeline_forward_3d; + P->inv3d = pipeline_reverse_3d; + P->fwd = pipeline_forward; + P->inv = pipeline_reverse; + P->destructor = destructor; + P->is_pipeline = 1; + + /* Currently, the pipeline driver is a raw bit mover, enabling other operations */ + /* to collaborate efficiently. All prep/fin stuff is done at the step levels. */ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + + + P->opaque = static_cast(pj_calloc (1, sizeof(struct pj_opaque))); + if (0==P->opaque) + return destructor(P, ENOMEM); + + argc = (int)argc_params (P->params); + static_cast(P->opaque)->argv = argv = argv_params (P->params, argc); + if (0==argv) + return destructor (P, ENOMEM); + + static_cast(P->opaque)->current_argv = current_argv = static_cast(pj_calloc (argc, sizeof (char *))); + if (0==current_argv) + return destructor (P, ENOMEM); + + /* Do some syntactical sanity checking */ + for (i = 0; i < argc; i++) { + if (0==strcmp (argv_sentinel, argv[i])) { + if (-1==i_pipeline) { + proj_log_error (P, "Pipeline: +step before +proj=pipeline"); + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); + } + if (0==nsteps) + i_first_step = i; + nsteps++; + continue; + } + + if (0==strcmp ("proj=pipeline", argv[i])) { + if (-1 != i_pipeline) { + proj_log_error (P, "Pipeline: Nesting only allowed when child pipelines are wrapped in '+init's"); + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: nested pipelines */ + } + i_pipeline = i; + } + } + nsteps--; /* Last instance of +step is just a sentinel */ + static_cast(P->opaque)->steps = nsteps; + + if (-1==i_pipeline) + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ + + if (0==nsteps) + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); /* ERROR: no pipeline def */ + + /* Make room for the pipeline and execution indicators */ + if (0==pj_create_pipeline (P, nsteps)) + return destructor (P, ENOMEM); + + set_ellipsoid(P); + + /* Now loop over all steps, building a new set of arguments for each init */ + i_current_step = i_first_step; + for (i = 0; i < nsteps; i++) { + int j; + int current_argc = 0; + int err; + PJ *next_step = 0; + + /* Build a set of setup args for the current step */ + proj_log_trace (P, "Pipeline: Building arg list for step no. %d", i); + + /* First add the step specific args */ + for (j = i_current_step + 1; 0 != strcmp ("step", argv[j]); j++) + current_argv[current_argc++] = argv[j]; + + i_current_step = j; + + /* Then add the global args */ + for (j = i_pipeline + 1; 0 != strcmp ("step", argv[j]); j++) + current_argv[current_argc++] = argv[j]; + + proj_log_trace (P, "Pipeline: init - %s, %d", current_argv[0], current_argc); + for (j = 1; j < current_argc; j++) + proj_log_trace (P, " %s", current_argv[j]); + + err = proj_errno_reset (P); + + next_step = proj_create_argv (P->ctx, current_argc, current_argv); + proj_log_trace (P, "Pipeline: Step %d (%s) at %p", i, current_argv[0], next_step); + + if (0==next_step) { + /* The step init failed, but possibly without setting errno. If so, we say "malformed" */ + int err_to_report = proj_errno(P); + if (0==err_to_report) + err_to_report = PJD_ERR_MALFORMED_PIPELINE; + proj_log_error (P, "Pipeline: Bad step definition: %s (%s)", current_argv[0], pj_strerrno (err_to_report)); + return destructor (P, err_to_report); /* ERROR: bad pipeline def */ + } + + proj_errno_restore (P, err); + + /* Is this step inverted? */ + for (j = 0; j < current_argc; j++) + if (0==strcmp("inv", current_argv[j])) { + /* if +inv exists in both global and local args the forward operation should be used */ + next_step->inverted = next_step->inverted == 0 ? 1 : 0; + } + + static_cast(P->opaque)->pipeline[i+1] = next_step; + + proj_log_trace (P, "Pipeline at [%p]: step at [%p] (%s) done", P, next_step, current_argv[0]); + } + + /* Require a forward path through the pipeline */ + for (i = 1; i <= nsteps; i++) { + PJ *Q = static_cast(P->opaque)->pipeline[i]; + if ( ( Q->inverted && (Q->inv || Q->inv3d || Q->fwd4d) ) || + (!Q->inverted && (Q->fwd || Q->fwd3d || Q->fwd4d) ) ) { + continue; + } else { + proj_log_error (P, "Pipeline: A forward operation couldn't be constructed"); + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); + } + } + + /* determine if an inverse operation is possible */ + for (i = 1; i <= nsteps; i++) { + PJ *Q = static_cast(P->opaque)->pipeline[i]; + if ( pj_has_inverse(Q) ) { + continue; + } else { + P->inv = 0; + P->inv3d = 0; + P->inv4d = 0; + break; + } + } + + /* Check that output units from step i are compatible with expected units in step i+1 */ + for (i = 1; i < nsteps; i++) { + enum pj_io_units unit_returned = pj_right (static_cast(P->opaque)->pipeline[i]); + enum pj_io_units unit_expected = pj_left (static_cast(P->opaque)->pipeline[i+1]); + + if ( unit_returned == PJ_IO_UNITS_WHATEVER || unit_expected == PJ_IO_UNITS_WHATEVER ) + continue; + if ( unit_returned != unit_expected ) { + proj_log_error (P, "Pipeline: Mismatched units between step %d and %d", i, i+1); + return destructor (P, PJD_ERR_MALFORMED_PIPELINE); + } + } + + proj_log_trace (P, "Pipeline: %d steps built. Determining i/o characteristics", nsteps); + + /* Determine forward input (= reverse output) data type */ + P->left = pj_left (static_cast(P->opaque)->pipeline[1]); + + /* Now, correspondingly determine forward output (= reverse input) data type */ + P->right = pj_right (static_cast(P->opaque)->pipeline[nsteps]); + return P; +} diff --git a/src/PJ_poly.c b/src/PJ_poly.c deleted file mode 100644 index b5f78fdf..00000000 --- a/src/PJ_poly.c +++ /dev/null @@ -1,169 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(poly, "Polyconic (American)") - "\n\tConic, Sph&Ell"; - -struct pj_opaque { - double ml0; \ - double *en; -}; - -#define TOL 1e-10 -#define CONV 1e-10 -#define N_ITER 10 -#define I_ITER 20 -#define ITOL 1.e-12 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double ms, sp, cp; - - if (fabs(lp.phi) <= TOL) { - xy.x = lp.lam; - xy.y = -Q->ml0; - } else { - sp = sin(lp.phi); - ms = fabs(cp = cos(lp.phi)) > TOL ? pj_msfn(sp, cp, P->es) / sp : 0.; - xy.x = ms * sin(lp.lam *= sp); - xy.y = (pj_mlfn(lp.phi, sp, cp, Q->en) - Q->ml0) + ms * (1. - cos(lp.lam)); - } - - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cot, E; - - if (fabs(lp.phi) <= TOL) { - xy.x = lp.lam; - xy.y = Q->ml0; - } else { - cot = 1. / tan(lp.phi); - xy.x = sin(E = lp.lam * sin(lp.phi)) * cot; - xy.y = lp.phi - P->phi0 + cot * (1. - cos(E)); - } - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.y += Q->ml0; - if (fabs(xy.y) <= TOL) { - lp.lam = xy.x; - lp.phi = 0.; - } else { - double r, c, sp, cp, s2ph, ml, mlb, mlp, dPhi; - int i; - - r = xy.y * xy.y + xy.x * xy.x; - lp.phi = xy.y; - for (i = I_ITER; i ; --i) { - sp = sin(lp.phi); - s2ph = sp * ( cp = cos(lp.phi)); - if (fabs(cp) < ITOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - c = sp * (mlp = sqrt(1. - P->es * sp * sp)) / cp; - ml = pj_mlfn(lp.phi, sp, cp, Q->en); - mlb = ml * ml + r; - mlp = P->one_es / (mlp * mlp * mlp); - lp.phi += ( dPhi = - ( ml + ml + c * mlb - 2. * xy.y * (c * ml + 1.) ) / ( - P->es * s2ph * (mlb - 2. * xy.y * ml) / c + - 2.* (xy.y - ml) * (c * mlp - 1. / s2ph) - mlp - mlp )); - if (fabs(dPhi) <= ITOL) - break; - } - if (!i) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - c = sin(lp.phi); - lp.lam = asin(xy.x * tan(lp.phi) * sqrt(1. - P->es * c * c)) / sin(lp.phi); - } - - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double B, dphi, tp; - int i; - - if (fabs(xy.y = P->phi0 + xy.y) <= TOL) { - lp.lam = xy.x; - lp.phi = 0.; - } else { - lp.phi = xy.y; - B = xy.x * xy.x + xy.y * xy.y; - i = N_ITER; - do { - tp = tan(lp.phi); - lp.phi -= (dphi = (xy.y * (lp.phi * tp + 1.) - lp.phi - - .5 * ( lp.phi * lp.phi + B) * tp) / - ((lp.phi - xy.y) / tp - 1.)); - } while (fabs(dphi) > CONV && --i); - if (! i) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - lp.lam = asin(xy.x * tan(lp.phi)) / sin(lp.phi); - } - - return lp; -} - - -static void *destructor(PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - if (P->opaque->en) - pj_dealloc (P->opaque->en); - - return pj_default_destructor(P, errlev); -} - - -PJ *PROJECTION(poly) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - - P->opaque = Q; - P->destructor = destructor; - - if (P->es != 0.0) { - if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor (P, ENOMEM); - Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - Q->ml0 = -P->phi0; - P->inv = s_inverse; - P->fwd = s_forward; - } - - return P; -} diff --git a/src/PJ_poly.cpp b/src/PJ_poly.cpp new file mode 100644 index 00000000..3bf7a8dd --- /dev/null +++ b/src/PJ_poly.cpp @@ -0,0 +1,169 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(poly, "Polyconic (American)") + "\n\tConic, Sph&Ell"; + +struct pj_opaque { + double ml0; \ + double *en; +}; + +#define TOL 1e-10 +#define CONV 1e-10 +#define N_ITER 10 +#define I_ITER 20 +#define ITOL 1.e-12 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double ms, sp, cp; + + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = -Q->ml0; + } else { + sp = sin(lp.phi); + ms = fabs(cp = cos(lp.phi)) > TOL ? pj_msfn(sp, cp, P->es) / sp : 0.; + xy.x = ms * sin(lp.lam *= sp); + xy.y = (pj_mlfn(lp.phi, sp, cp, Q->en) - Q->ml0) + ms * (1. - cos(lp.lam)); + } + + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cot, E; + + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = Q->ml0; + } else { + cot = 1. / tan(lp.phi); + xy.x = sin(E = lp.lam * sin(lp.phi)) * cot; + xy.y = lp.phi - P->phi0 + cot * (1. - cos(E)); + } + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.y += Q->ml0; + if (fabs(xy.y) <= TOL) { + lp.lam = xy.x; + lp.phi = 0.; + } else { + double r, c, sp, cp, s2ph, ml, mlb, mlp, dPhi; + int i; + + r = xy.y * xy.y + xy.x * xy.x; + lp.phi = xy.y; + for (i = I_ITER; i ; --i) { + sp = sin(lp.phi); + s2ph = sp * ( cp = cos(lp.phi)); + if (fabs(cp) < ITOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + c = sp * (mlp = sqrt(1. - P->es * sp * sp)) / cp; + ml = pj_mlfn(lp.phi, sp, cp, Q->en); + mlb = ml * ml + r; + mlp = P->one_es / (mlp * mlp * mlp); + lp.phi += ( dPhi = + ( ml + ml + c * mlb - 2. * xy.y * (c * ml + 1.) ) / ( + P->es * s2ph * (mlb - 2. * xy.y * ml) / c + + 2.* (xy.y - ml) * (c * mlp - 1. / s2ph) - mlp - mlp )); + if (fabs(dPhi) <= ITOL) + break; + } + if (!i) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + c = sin(lp.phi); + lp.lam = asin(xy.x * tan(lp.phi) * sqrt(1. - P->es * c * c)) / sin(lp.phi); + } + + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double B, dphi, tp; + int i; + + if (fabs(xy.y = P->phi0 + xy.y) <= TOL) { + lp.lam = xy.x; + lp.phi = 0.; + } else { + lp.phi = xy.y; + B = xy.x * xy.x + xy.y * xy.y; + i = N_ITER; + do { + tp = tan(lp.phi); + lp.phi -= (dphi = (xy.y * (lp.phi * tp + 1.) - lp.phi - + .5 * ( lp.phi * lp.phi + B) * tp) / + ((lp.phi - xy.y) / tp - 1.)); + } while (fabs(dphi) > CONV && --i); + if (! i) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + lp.lam = asin(xy.x * tan(lp.phi)) / sin(lp.phi); + } + + return lp; +} + + +static PJ *destructor(PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + if (static_cast(P->opaque)->en) + pj_dealloc (static_cast(P->opaque)->en); + + return pj_default_destructor(P, errlev); +} + + +PJ *PROJECTION(poly) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + + P->opaque = Q; + P->destructor = destructor; + + if (P->es != 0.0) { + if (!(Q->en = pj_enfn(P->es))) + return pj_default_destructor (P, ENOMEM); + Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->ml0 = -P->phi0; + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} diff --git a/src/PJ_putp2.c b/src/PJ_putp2.c deleted file mode 100644 index d7a847c8..00000000 --- a/src/PJ_putp2.c +++ /dev/null @@ -1,61 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(putp2, "Putnins P2") "\n\tPCyl, Sph"; - -#define C_x 1.89490 -#define C_y 1.71848 -#define C_p 0.6141848493043784 -#define EPS 1e-10 -#define NITER 10 -#define PI_DIV_3 1.0471975511965977 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double p, c, s, V; - int i; - (void) P; - - p = C_p * sin(lp.phi); - s = lp.phi * lp.phi; - lp.phi *= 0.615709 + s * ( 0.00909953 + s * 0.0046292 ); - for (i = NITER; i ; --i) { - c = cos(lp.phi); - s = sin(lp.phi); - lp.phi -= V = (lp.phi + s * (c - 1.) - p) / - (1. + c * (c - 1.) - s * s); - if (fabs(V) < EPS) - break; - } - if (!i) - lp.phi = lp.phi < 0 ? - PI_DIV_3 : PI_DIV_3; - xy.x = C_x * lp.lam * (cos(lp.phi) - 0.5); - xy.y = C_y * sin(lp.phi); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double c; - - lp.phi = aasin(P->ctx,xy.y / C_y); - lp.lam = xy.x / (C_x * ((c = cos(lp.phi)) - 0.5)); - lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c - 1.)) / C_p); - - return lp; -} - - -PJ *PROJECTION(putp2) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_putp2.cpp b/src/PJ_putp2.cpp new file mode 100644 index 00000000..d7a847c8 --- /dev/null +++ b/src/PJ_putp2.cpp @@ -0,0 +1,61 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(putp2, "Putnins P2") "\n\tPCyl, Sph"; + +#define C_x 1.89490 +#define C_y 1.71848 +#define C_p 0.6141848493043784 +#define EPS 1e-10 +#define NITER 10 +#define PI_DIV_3 1.0471975511965977 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double p, c, s, V; + int i; + (void) P; + + p = C_p * sin(lp.phi); + s = lp.phi * lp.phi; + lp.phi *= 0.615709 + s * ( 0.00909953 + s * 0.0046292 ); + for (i = NITER; i ; --i) { + c = cos(lp.phi); + s = sin(lp.phi); + lp.phi -= V = (lp.phi + s * (c - 1.) - p) / + (1. + c * (c - 1.) - s * s); + if (fabs(V) < EPS) + break; + } + if (!i) + lp.phi = lp.phi < 0 ? - PI_DIV_3 : PI_DIV_3; + xy.x = C_x * lp.lam * (cos(lp.phi) - 0.5); + xy.y = C_y * sin(lp.phi); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double c; + + lp.phi = aasin(P->ctx,xy.y / C_y); + lp.lam = xy.x / (C_x * ((c = cos(lp.phi)) - 0.5)); + lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c - 1.)) / C_p); + + return lp; +} + + +PJ *PROJECTION(putp2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_putp3.c b/src/PJ_putp3.c deleted file mode 100644 index 695ea877..00000000 --- a/src/PJ_putp3.c +++ /dev/null @@ -1,65 +0,0 @@ -#define PJ_LIB__ -#include -#include "projects.h" - -struct pj_opaque { - double A; -}; - -PROJ_HEAD(putp3, "Putnins P3") "\n\tPCyl, Sph"; -PROJ_HEAD(putp3p, "Putnins P3'") "\n\tPCyl, Sph"; - -#define C 0.79788456 -#define RPISQ 0.1013211836 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - - xy.x = C * lp.lam * (1. - P->opaque->A * lp.phi * lp.phi); - xy.y = C * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - - lp.phi = xy.y / C; - lp.lam = xy.x / (C * (1. - P->opaque->A * lp.phi * lp.phi)); - - return lp; -} - - -PJ *PROJECTION(putp3) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->A = 4. * RPISQ; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - -PJ *PROJECTION(putp3p) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->A = 2. * RPISQ; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - diff --git a/src/PJ_putp3.cpp b/src/PJ_putp3.cpp new file mode 100644 index 00000000..6e85d35f --- /dev/null +++ b/src/PJ_putp3.cpp @@ -0,0 +1,65 @@ +#define PJ_LIB__ +#include +#include "projects.h" + +struct pj_opaque { + double A; +}; + +PROJ_HEAD(putp3, "Putnins P3") "\n\tPCyl, Sph"; +PROJ_HEAD(putp3p, "Putnins P3'") "\n\tPCyl, Sph"; + +#define C 0.79788456 +#define RPISQ 0.1013211836 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + + xy.x = C * lp.lam * (1. - static_cast(P->opaque)->A * lp.phi * lp.phi); + xy.y = C * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + + lp.phi = xy.y / C; + lp.lam = xy.x / (C * (1. - static_cast(P->opaque)->A * lp.phi * lp.phi)); + + return lp; +} + + +PJ *PROJECTION(putp3) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->A = 4. * RPISQ; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +PJ *PROJECTION(putp3p) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->A = 2. * RPISQ; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + diff --git a/src/PJ_putp4p.c b/src/PJ_putp4p.c deleted file mode 100644 index 6448dc19..00000000 --- a/src/PJ_putp4p.c +++ /dev/null @@ -1,74 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -struct pj_opaque { - double C_x, C_y; -}; - -PROJ_HEAD(putp4p, "Putnins P4'") "\n\tPCyl, Sph"; -PROJ_HEAD(weren, "Werenskiold I") "\n\tPCyl, Sph"; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - lp.phi = aasin(P->ctx,0.883883476 * sin(lp.phi)); - xy.x = Q->C_x * lp.lam * cos(lp.phi); - xy.x /= cos(lp.phi *= 0.333333333333333); - xy.y = Q->C_y * sin(lp.phi); - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - lp.phi = aasin(P->ctx,xy.y / Q->C_y); - lp.lam = xy.x * cos(lp.phi) / Q->C_x; - lp.phi *= 3.; - lp.lam /= cos(lp.phi); - lp.phi = aasin(P->ctx,1.13137085 * sin(lp.phi)); - - return lp; -} - - -PJ *PROJECTION(putp4p) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->C_x = 0.874038744; - Q->C_y = 3.883251825; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - - -PJ *PROJECTION(weren) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->C_x = 1.; - Q->C_y = 4.442882938; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_putp4p.cpp b/src/PJ_putp4p.cpp new file mode 100644 index 00000000..77a18651 --- /dev/null +++ b/src/PJ_putp4p.cpp @@ -0,0 +1,74 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +struct pj_opaque { + double C_x, C_y; +}; + +PROJ_HEAD(putp4p, "Putnins P4'") "\n\tPCyl, Sph"; +PROJ_HEAD(weren, "Werenskiold I") "\n\tPCyl, Sph"; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + lp.phi = aasin(P->ctx,0.883883476 * sin(lp.phi)); + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.x /= cos(lp.phi *= 0.333333333333333); + xy.y = Q->C_y * sin(lp.phi); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + lp.phi = aasin(P->ctx,xy.y / Q->C_y); + lp.lam = xy.x * cos(lp.phi) / Q->C_x; + lp.phi *= 3.; + lp.lam /= cos(lp.phi); + lp.phi = aasin(P->ctx,1.13137085 * sin(lp.phi)); + + return lp; +} + + +PJ *PROJECTION(putp4p) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->C_x = 0.874038744; + Q->C_y = 3.883251825; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(weren) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->C_x = 1.; + Q->C_y = 4.442882938; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_putp5.c b/src/PJ_putp5.c deleted file mode 100644 index 96b0670d..00000000 --- a/src/PJ_putp5.c +++ /dev/null @@ -1,73 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -struct pj_opaque { - double A, B; -}; - -PROJ_HEAD(putp5, "Putnins P5") "\n\tPCyl, Sph"; -PROJ_HEAD(putp5p, "Putnins P5'") "\n\tPCyl, Sph"; - -#define C 1.01346 -#define D 1.2158542 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - xy.x = C * lp.lam * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi)); - xy.y = C * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - - lp.phi = xy.y / C; - lp.lam = xy.x / (C * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi))); - - return lp; -} - - - -PJ *PROJECTION(putp5) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->A = 2.; - Q->B = 1.; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - - -PJ *PROJECTION(putp5p) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->A = 1.5; - Q->B = 0.5; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_putp5.cpp b/src/PJ_putp5.cpp new file mode 100644 index 00000000..d73e9368 --- /dev/null +++ b/src/PJ_putp5.cpp @@ -0,0 +1,73 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +struct pj_opaque { + double A, B; +}; + +PROJ_HEAD(putp5, "Putnins P5") "\n\tPCyl, Sph"; +PROJ_HEAD(putp5p, "Putnins P5'") "\n\tPCyl, Sph"; + +#define C 1.01346 +#define D 1.2158542 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + xy.x = C * lp.lam * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi)); + xy.y = C * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + + lp.phi = xy.y / C; + lp.lam = xy.x / (C * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi))); + + return lp; +} + + + +PJ *PROJECTION(putp5) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->A = 2.; + Q->B = 1.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(putp5p) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->A = 1.5; + Q->B = 0.5; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_putp6.c b/src/PJ_putp6.c deleted file mode 100644 index fa9290b4..00000000 --- a/src/PJ_putp6.c +++ /dev/null @@ -1,95 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -struct pj_opaque { - double C_x, C_y, A, B, D; -}; - -PROJ_HEAD(putp6, "Putnins P6") "\n\tPCyl, Sph"; -PROJ_HEAD(putp6p, "Putnins P6'") "\n\tPCyl, Sph"; - -#define EPS 1e-10 -#define NITER 10 -#define CON_POLE 1.732050807568877 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double p, r, V; - int i; - - p = Q->B * sin(lp.phi); - lp.phi *= 1.10265779; - for (i = NITER; i ; --i) { - r = sqrt(1. + lp.phi * lp.phi); - lp.phi -= V = ( (Q->A - r) * lp.phi - log(lp.phi + r) - p ) / - (Q->A - 2. * r); - if (fabs(V) < EPS) - break; - } - if (!i) - lp.phi = p < 0. ? -CON_POLE : CON_POLE; - xy.x = Q->C_x * lp.lam * (Q->D - sqrt(1. + lp.phi * lp.phi)); - xy.y = Q->C_y * lp.phi; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double r; - - lp.phi = xy.y / Q->C_y; - r = sqrt(1. + lp.phi * lp.phi); - lp.lam = xy.x / (Q->C_x * (Q->D - r)); - lp.phi = aasin( P->ctx, ( (Q->A - r) * lp.phi - log(lp.phi + r) ) / Q->B); - - return lp; -} - - -PJ *PROJECTION(putp6) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->C_x = 1.01346; - Q->C_y = 0.91910; - Q->A = 4.; - Q->B = 2.1471437182129378784; - Q->D = 2.; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - - -PJ *PROJECTION(putp6p) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->C_x = 0.44329; - Q->C_y = 0.80404; - Q->A = 6.; - Q->B = 5.61125; - Q->D = 3.; - - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_putp6.cpp b/src/PJ_putp6.cpp new file mode 100644 index 00000000..fcd8146f --- /dev/null +++ b/src/PJ_putp6.cpp @@ -0,0 +1,95 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +struct pj_opaque { + double C_x, C_y, A, B, D; +}; + +PROJ_HEAD(putp6, "Putnins P6") "\n\tPCyl, Sph"; +PROJ_HEAD(putp6p, "Putnins P6'") "\n\tPCyl, Sph"; + +#define EPS 1e-10 +#define NITER 10 +#define CON_POLE 1.732050807568877 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double p, r, V; + int i; + + p = Q->B * sin(lp.phi); + lp.phi *= 1.10265779; + for (i = NITER; i ; --i) { + r = sqrt(1. + lp.phi * lp.phi); + lp.phi -= V = ( (Q->A - r) * lp.phi - log(lp.phi + r) - p ) / + (Q->A - 2. * r); + if (fabs(V) < EPS) + break; + } + if (!i) + lp.phi = p < 0. ? -CON_POLE : CON_POLE; + xy.x = Q->C_x * lp.lam * (Q->D - sqrt(1. + lp.phi * lp.phi)); + xy.y = Q->C_y * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double r; + + lp.phi = xy.y / Q->C_y; + r = sqrt(1. + lp.phi * lp.phi); + lp.lam = xy.x / (Q->C_x * (Q->D - r)); + lp.phi = aasin( P->ctx, ( (Q->A - r) * lp.phi - log(lp.phi + r) ) / Q->B); + + return lp; +} + + +PJ *PROJECTION(putp6) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->C_x = 1.01346; + Q->C_y = 0.91910; + Q->A = 4.; + Q->B = 2.1471437182129378784; + Q->D = 2.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(putp6p) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->C_x = 0.44329; + Q->C_y = 0.80404; + Q->A = 6.; + Q->B = 5.61125; + Q->D = 3.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_qsc.c b/src/PJ_qsc.c deleted file mode 100644 index 3b8b7fe2..00000000 --- a/src/PJ_qsc.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - * This implements the Quadrilateralized Spherical Cube (QSC) projection. - * - * Copyright (c) 2011, 2012 Martin Lambers - * - * The QSC projection was introduced in: - * [OL76] - * E.M. O'Neill and R.E. Laubscher, "Extended Studies of a Quadrilateralized - * Spherical Cube Earth Data Base", Naval Environmental Prediction Research - * Facility Tech. Report NEPRF 3-76 (CSC), May 1976. - * - * The preceding shift from an ellipsoid to a sphere, which allows to apply - * this projection to ellipsoids as used in the Ellipsoidal Cube Map model, - * is described in - * [LK12] - * M. Lambers and A. Kolb, "Ellipsoidal Cube Maps for Accurate Rendering of - * Planetary-Scale Terrain Data", Proc. Pacific Graphics (Short Papers), Sep. - * 2012 - * - * You have to choose one of the following projection centers, - * corresponding to the centers of the six cube faces: - * phi0 = 0.0, lam0 = 0.0 ("front" face) - * phi0 = 0.0, lam0 = 90.0 ("right" face) - * phi0 = 0.0, lam0 = 180.0 ("back" face) - * phi0 = 0.0, lam0 = -90.0 ("left" face) - * phi0 = 90.0 ("top" face) - * phi0 = -90.0 ("bottom" face) - * Other projection centers will not work! - * - * In the projection code below, each cube face is handled differently. - * See the computation of the face parameter in the PROJECTION(qsc) function - * and the handling of different face values (FACE_*) in the forward and - * inverse projections. - * - * Furthermore, the projection is originally only defined for theta angles - * between (-1/4 * PI) and (+1/4 * PI) on the current cube face. This area - * of definition is named AREA_0 in the projection code below. The other - * three areas of a cube face are handled by rotation of AREA_0. - */ - -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -/* The six cube faces. */ -enum Face { - FACE_FRONT = 0, - FACE_RIGHT = 1, - FACE_BACK = 2, - FACE_LEFT = 3, - FACE_TOP = 4, - FACE_BOTTOM = 5 -}; - -struct pj_opaque { - enum Face face; - double a_squared; - double b; - double one_minus_f; - double one_minus_f_squared; -}; -PROJ_HEAD(qsc, "Quadrilateralized Spherical Cube") "\n\tAzi, Sph"; - -#define EPS10 1.e-10 - -/* The four areas on a cube face. AREA_0 is the area of definition, - * the other three areas are counted counterclockwise. */ -enum Area { - AREA_0 = 0, - AREA_1 = 1, - AREA_2 = 2, - AREA_3 = 3 -}; - -/* Helper function for forward projection: compute the theta angle - * and determine the area number. */ -static double qsc_fwd_equat_face_theta(double phi, double y, double x, enum Area *area) { - double theta; - if (phi < EPS10) { - *area = AREA_0; - theta = 0.0; - } else { - theta = atan2(y, x); - if (fabs(theta) <= M_FORTPI) { - *area = AREA_0; - } else if (theta > M_FORTPI && theta <= M_HALFPI + M_FORTPI) { - *area = AREA_1; - theta -= M_HALFPI; - } else if (theta > M_HALFPI + M_FORTPI || theta <= -(M_HALFPI + M_FORTPI)) { - *area = AREA_2; - theta = (theta >= 0.0 ? theta - M_PI : theta + M_PI); - } else { - *area = AREA_3; - theta += M_HALFPI; - } - } - return theta; -} - -/* Helper function: shift the longitude. */ -static double qsc_shift_lon_origin(double lon, double offset) { - double slon = lon + offset; - if (slon < -M_PI) { - slon += M_TWOPI; - } else if (slon > +M_PI) { - slon -= M_TWOPI; - } - return slon; -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double lat, lon; - double theta, phi; - double t, mu; /* nu; */ - enum Area area; - - /* Convert the geodetic latitude to a geocentric latitude. - * This corresponds to the shift from the ellipsoid to the sphere - * described in [LK12]. */ - if (P->es != 0.0) { - lat = atan(Q->one_minus_f_squared * tan(lp.phi)); - } else { - lat = lp.phi; - } - - /* Convert the input lat, lon into theta, phi as used by QSC. - * This depends on the cube face and the area on it. - * For the top and bottom face, we can compute theta and phi - * directly from phi, lam. For the other faces, we must use - * unit sphere cartesian coordinates as an intermediate step. */ - lon = lp.lam; - if (Q->face == FACE_TOP) { - phi = M_HALFPI - lat; - if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) { - area = AREA_0; - theta = lon - M_HALFPI; - } else if (lon > M_HALFPI + M_FORTPI || lon <= -(M_HALFPI + M_FORTPI)) { - area = AREA_1; - theta = (lon > 0.0 ? lon - M_PI : lon + M_PI); - } else if (lon > -(M_HALFPI + M_FORTPI) && lon <= -M_FORTPI) { - area = AREA_2; - theta = lon + M_HALFPI; - } else { - area = AREA_3; - theta = lon; - } - } else if (Q->face == FACE_BOTTOM) { - phi = M_HALFPI + lat; - if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) { - area = AREA_0; - theta = -lon + M_HALFPI; - } else if (lon < M_FORTPI && lon >= -M_FORTPI) { - area = AREA_1; - theta = -lon; - } else if (lon < -M_FORTPI && lon >= -(M_HALFPI + M_FORTPI)) { - area = AREA_2; - theta = -lon - M_HALFPI; - } else { - area = AREA_3; - theta = (lon > 0.0 ? -lon + M_PI : -lon - M_PI); - } - } else { - double q, r, s; - double sinlat, coslat; - double sinlon, coslon; - - if (Q->face == FACE_RIGHT) { - lon = qsc_shift_lon_origin(lon, +M_HALFPI); - } else if (Q->face == FACE_BACK) { - lon = qsc_shift_lon_origin(lon, +M_PI); - } else if (Q->face == FACE_LEFT) { - lon = qsc_shift_lon_origin(lon, -M_HALFPI); - } - sinlat = sin(lat); - coslat = cos(lat); - sinlon = sin(lon); - coslon = cos(lon); - q = coslat * coslon; - r = coslat * sinlon; - s = sinlat; - - if (Q->face == FACE_FRONT) { - phi = acos(q); - theta = qsc_fwd_equat_face_theta(phi, s, r, &area); - } else if (Q->face == FACE_RIGHT) { - phi = acos(r); - theta = qsc_fwd_equat_face_theta(phi, s, -q, &area); - } else if (Q->face == FACE_BACK) { - phi = acos(-q); - theta = qsc_fwd_equat_face_theta(phi, s, -r, &area); - } else if (Q->face == FACE_LEFT) { - phi = acos(-r); - theta = qsc_fwd_equat_face_theta(phi, s, q, &area); - } else { - /* Impossible */ - phi = theta = 0.0; - area = AREA_0; - } - } - - /* Compute mu and nu for the area of definition. - * For mu, see Eq. (3-21) in [OL76], but note the typos: - * compare with Eq. (3-14). For nu, see Eq. (3-38). */ - mu = atan((12.0 / M_PI) * (theta + acos(sin(theta) * cos(M_FORTPI)) - M_HALFPI)); - t = sqrt((1.0 - cos(phi)) / (cos(mu) * cos(mu)) / (1.0 - cos(atan(1.0 / cos(theta))))); - /* nu = atan(t); We don't really need nu, just t, see below. */ - - /* Apply the result to the real area. */ - if (area == AREA_1) { - mu += M_HALFPI; - } else if (area == AREA_2) { - mu += M_PI; - } else if (area == AREA_3) { - mu += M_PI_HALFPI; - } - - /* Now compute x, y from mu and nu */ - /* t = tan(nu); */ - xy.x = t * cos(mu); - xy.y = t * sin(mu); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double mu, nu, cosmu, tannu; - double tantheta, theta, cosphi, phi; - double t; - int area; - - /* Convert the input x, y to the mu and nu angles as used by QSC. - * This depends on the area of the cube face. */ - nu = atan(sqrt(xy.x * xy.x + xy.y * xy.y)); - mu = atan2(xy.y, xy.x); - if (xy.x >= 0.0 && xy.x >= fabs(xy.y)) { - area = AREA_0; - } else if (xy.y >= 0.0 && xy.y >= fabs(xy.x)) { - area = AREA_1; - mu -= M_HALFPI; - } else if (xy.x < 0.0 && -xy.x >= fabs(xy.y)) { - area = AREA_2; - mu = (mu < 0.0 ? mu + M_PI : mu - M_PI); - } else { - area = AREA_3; - mu += M_HALFPI; - } - - /* Compute phi and theta for the area of definition. - * The inverse projection is not described in the original paper, but some - * good hints can be found here (as of 2011-12-14): - * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302 - * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */ - t = (M_PI / 12.0) * tan(mu); - tantheta = sin(t) / (cos(t) - (1.0 / sqrt(2.0))); - theta = atan(tantheta); - cosmu = cos(mu); - tannu = tan(nu); - cosphi = 1.0 - cosmu * cosmu * tannu * tannu * (1.0 - cos(atan(1.0 / cos(theta)))); - if (cosphi < -1.0) { - cosphi = -1.0; - } else if (cosphi > +1.0) { - cosphi = +1.0; - } - - /* Apply the result to the real area on the cube face. - * For the top and bottom face, we can compute phi and lam directly. - * For the other faces, we must use unit sphere cartesian coordinates - * as an intermediate step. */ - if (Q->face == FACE_TOP) { - phi = acos(cosphi); - lp.phi = M_HALFPI - phi; - if (area == AREA_0) { - lp.lam = theta + M_HALFPI; - } else if (area == AREA_1) { - lp.lam = (theta < 0.0 ? theta + M_PI : theta - M_PI); - } else if (area == AREA_2) { - lp.lam = theta - M_HALFPI; - } else /* area == AREA_3 */ { - lp.lam = theta; - } - } else if (Q->face == FACE_BOTTOM) { - phi = acos(cosphi); - lp.phi = phi - M_HALFPI; - if (area == AREA_0) { - lp.lam = -theta + M_HALFPI; - } else if (area == AREA_1) { - lp.lam = -theta; - } else if (area == AREA_2) { - lp.lam = -theta - M_HALFPI; - } else /* area == AREA_3 */ { - lp.lam = (theta < 0.0 ? -theta - M_PI : -theta + M_PI); - } - } else { - /* Compute phi and lam via cartesian unit sphere coordinates. */ - double q, r, s; - q = cosphi; - t = q * q; - if (t >= 1.0) { - s = 0.0; - } else { - s = sqrt(1.0 - t) * sin(theta); - } - t += s * s; - if (t >= 1.0) { - r = 0.0; - } else { - r = sqrt(1.0 - t); - } - /* Rotate q,r,s into the correct area. */ - if (area == AREA_1) { - t = r; - r = -s; - s = t; - } else if (area == AREA_2) { - r = -r; - s = -s; - } else if (area == AREA_3) { - t = r; - r = s; - s = -t; - } - /* Rotate q,r,s into the correct cube face. */ - if (Q->face == FACE_RIGHT) { - t = q; - q = -r; - r = t; - } else if (Q->face == FACE_BACK) { - q = -q; - r = -r; - } else if (Q->face == FACE_LEFT) { - t = q; - q = r; - r = -t; - } - /* Now compute phi and lam from the unit sphere coordinates. */ - lp.phi = acos(-s) - M_HALFPI; - lp.lam = atan2(r, q); - if (Q->face == FACE_RIGHT) { - lp.lam = qsc_shift_lon_origin(lp.lam, -M_HALFPI); - } else if (Q->face == FACE_BACK) { - lp.lam = qsc_shift_lon_origin(lp.lam, -M_PI); - } else if (Q->face == FACE_LEFT) { - lp.lam = qsc_shift_lon_origin(lp.lam, +M_HALFPI); - } - } - - /* Apply the shift from the sphere to the ellipsoid as described - * in [LK12]. */ - if (P->es != 0.0) { - int invert_sign; - double tanphi, xa; - invert_sign = (lp.phi < 0.0 ? 1 : 0); - tanphi = tan(lp.phi); - xa = Q->b / sqrt(tanphi * tanphi + Q->one_minus_f_squared); - lp.phi = atan(sqrt(P->a * P->a - xa * xa) / (Q->one_minus_f * xa)); - if (invert_sign) { - lp.phi = -lp.phi; - } - } - return lp; -} - - -PJ *PROJECTION(qsc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - P->inv = e_inverse; - P->fwd = e_forward; - /* Determine the cube face from the center of projection. */ - if (P->phi0 >= M_HALFPI - M_FORTPI / 2.0) { - Q->face = FACE_TOP; - } else if (P->phi0 <= -(M_HALFPI - M_FORTPI / 2.0)) { - Q->face = FACE_BOTTOM; - } else if (fabs(P->lam0) <= M_FORTPI) { - Q->face = FACE_FRONT; - } else if (fabs(P->lam0) <= M_HALFPI + M_FORTPI) { - Q->face = (P->lam0 > 0.0 ? FACE_RIGHT : FACE_LEFT); - } else { - Q->face = FACE_BACK; - } - /* Fill in useful values for the ellipsoid <-> sphere shift - * described in [LK12]. */ - if (P->es != 0.0) { - Q->a_squared = P->a * P->a; - Q->b = P->a * sqrt(1.0 - P->es); - Q->one_minus_f = 1.0 - (P->a - Q->b) / P->a; - Q->one_minus_f_squared = Q->one_minus_f * Q->one_minus_f; - } - - return P; -} diff --git a/src/PJ_qsc.cpp b/src/PJ_qsc.cpp new file mode 100644 index 00000000..767ed4a8 --- /dev/null +++ b/src/PJ_qsc.cpp @@ -0,0 +1,402 @@ +/* + * This implements the Quadrilateralized Spherical Cube (QSC) projection. + * + * Copyright (c) 2011, 2012 Martin Lambers + * + * The QSC projection was introduced in: + * [OL76] + * E.M. O'Neill and R.E. Laubscher, "Extended Studies of a Quadrilateralized + * Spherical Cube Earth Data Base", Naval Environmental Prediction Research + * Facility Tech. Report NEPRF 3-76 (CSC), May 1976. + * + * The preceding shift from an ellipsoid to a sphere, which allows to apply + * this projection to ellipsoids as used in the Ellipsoidal Cube Map model, + * is described in + * [LK12] + * M. Lambers and A. Kolb, "Ellipsoidal Cube Maps for Accurate Rendering of + * Planetary-Scale Terrain Data", Proc. Pacific Graphics (Short Papers), Sep. + * 2012 + * + * You have to choose one of the following projection centers, + * corresponding to the centers of the six cube faces: + * phi0 = 0.0, lam0 = 0.0 ("front" face) + * phi0 = 0.0, lam0 = 90.0 ("right" face) + * phi0 = 0.0, lam0 = 180.0 ("back" face) + * phi0 = 0.0, lam0 = -90.0 ("left" face) + * phi0 = 90.0 ("top" face) + * phi0 = -90.0 ("bottom" face) + * Other projection centers will not work! + * + * In the projection code below, each cube face is handled differently. + * See the computation of the face parameter in the PROJECTION(qsc) function + * and the handling of different face values (FACE_*) in the forward and + * inverse projections. + * + * Furthermore, the projection is originally only defined for theta angles + * between (-1/4 * PI) and (+1/4 * PI) on the current cube face. This area + * of definition is named AREA_0 in the projection code below. The other + * three areas of a cube face are handled by rotation of AREA_0. + */ + +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +/* The six cube faces. */ +enum Face { + FACE_FRONT = 0, + FACE_RIGHT = 1, + FACE_BACK = 2, + FACE_LEFT = 3, + FACE_TOP = 4, + FACE_BOTTOM = 5 +}; + +struct pj_opaque { + enum Face face; + double a_squared; + double b; + double one_minus_f; + double one_minus_f_squared; +}; +PROJ_HEAD(qsc, "Quadrilateralized Spherical Cube") "\n\tAzi, Sph"; + +#define EPS10 1.e-10 + +/* The four areas on a cube face. AREA_0 is the area of definition, + * the other three areas are counted counterclockwise. */ +enum Area { + AREA_0 = 0, + AREA_1 = 1, + AREA_2 = 2, + AREA_3 = 3 +}; + +/* Helper function for forward projection: compute the theta angle + * and determine the area number. */ +static double qsc_fwd_equat_face_theta(double phi, double y, double x, enum Area *area) { + double theta; + if (phi < EPS10) { + *area = AREA_0; + theta = 0.0; + } else { + theta = atan2(y, x); + if (fabs(theta) <= M_FORTPI) { + *area = AREA_0; + } else if (theta > M_FORTPI && theta <= M_HALFPI + M_FORTPI) { + *area = AREA_1; + theta -= M_HALFPI; + } else if (theta > M_HALFPI + M_FORTPI || theta <= -(M_HALFPI + M_FORTPI)) { + *area = AREA_2; + theta = (theta >= 0.0 ? theta - M_PI : theta + M_PI); + } else { + *area = AREA_3; + theta += M_HALFPI; + } + } + return theta; +} + +/* Helper function: shift the longitude. */ +static double qsc_shift_lon_origin(double lon, double offset) { + double slon = lon + offset; + if (slon < -M_PI) { + slon += M_TWOPI; + } else if (slon > +M_PI) { + slon -= M_TWOPI; + } + return slon; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double lat, lon; + double theta, phi; + double t, mu; /* nu; */ + enum Area area; + + /* Convert the geodetic latitude to a geocentric latitude. + * This corresponds to the shift from the ellipsoid to the sphere + * described in [LK12]. */ + if (P->es != 0.0) { + lat = atan(Q->one_minus_f_squared * tan(lp.phi)); + } else { + lat = lp.phi; + } + + /* Convert the input lat, lon into theta, phi as used by QSC. + * This depends on the cube face and the area on it. + * For the top and bottom face, we can compute theta and phi + * directly from phi, lam. For the other faces, we must use + * unit sphere cartesian coordinates as an intermediate step. */ + lon = lp.lam; + if (Q->face == FACE_TOP) { + phi = M_HALFPI - lat; + if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) { + area = AREA_0; + theta = lon - M_HALFPI; + } else if (lon > M_HALFPI + M_FORTPI || lon <= -(M_HALFPI + M_FORTPI)) { + area = AREA_1; + theta = (lon > 0.0 ? lon - M_PI : lon + M_PI); + } else if (lon > -(M_HALFPI + M_FORTPI) && lon <= -M_FORTPI) { + area = AREA_2; + theta = lon + M_HALFPI; + } else { + area = AREA_3; + theta = lon; + } + } else if (Q->face == FACE_BOTTOM) { + phi = M_HALFPI + lat; + if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) { + area = AREA_0; + theta = -lon + M_HALFPI; + } else if (lon < M_FORTPI && lon >= -M_FORTPI) { + area = AREA_1; + theta = -lon; + } else if (lon < -M_FORTPI && lon >= -(M_HALFPI + M_FORTPI)) { + area = AREA_2; + theta = -lon - M_HALFPI; + } else { + area = AREA_3; + theta = (lon > 0.0 ? -lon + M_PI : -lon - M_PI); + } + } else { + double q, r, s; + double sinlat, coslat; + double sinlon, coslon; + + if (Q->face == FACE_RIGHT) { + lon = qsc_shift_lon_origin(lon, +M_HALFPI); + } else if (Q->face == FACE_BACK) { + lon = qsc_shift_lon_origin(lon, +M_PI); + } else if (Q->face == FACE_LEFT) { + lon = qsc_shift_lon_origin(lon, -M_HALFPI); + } + sinlat = sin(lat); + coslat = cos(lat); + sinlon = sin(lon); + coslon = cos(lon); + q = coslat * coslon; + r = coslat * sinlon; + s = sinlat; + + if (Q->face == FACE_FRONT) { + phi = acos(q); + theta = qsc_fwd_equat_face_theta(phi, s, r, &area); + } else if (Q->face == FACE_RIGHT) { + phi = acos(r); + theta = qsc_fwd_equat_face_theta(phi, s, -q, &area); + } else if (Q->face == FACE_BACK) { + phi = acos(-q); + theta = qsc_fwd_equat_face_theta(phi, s, -r, &area); + } else if (Q->face == FACE_LEFT) { + phi = acos(-r); + theta = qsc_fwd_equat_face_theta(phi, s, q, &area); + } else { + /* Impossible */ + phi = theta = 0.0; + area = AREA_0; + } + } + + /* Compute mu and nu for the area of definition. + * For mu, see Eq. (3-21) in [OL76], but note the typos: + * compare with Eq. (3-14). For nu, see Eq. (3-38). */ + mu = atan((12.0 / M_PI) * (theta + acos(sin(theta) * cos(M_FORTPI)) - M_HALFPI)); + t = sqrt((1.0 - cos(phi)) / (cos(mu) * cos(mu)) / (1.0 - cos(atan(1.0 / cos(theta))))); + /* nu = atan(t); We don't really need nu, just t, see below. */ + + /* Apply the result to the real area. */ + if (area == AREA_1) { + mu += M_HALFPI; + } else if (area == AREA_2) { + mu += M_PI; + } else if (area == AREA_3) { + mu += M_PI_HALFPI; + } + + /* Now compute x, y from mu and nu */ + /* t = tan(nu); */ + xy.x = t * cos(mu); + xy.y = t * sin(mu); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double mu, nu, cosmu, tannu; + double tantheta, theta, cosphi, phi; + double t; + int area; + + /* Convert the input x, y to the mu and nu angles as used by QSC. + * This depends on the area of the cube face. */ + nu = atan(sqrt(xy.x * xy.x + xy.y * xy.y)); + mu = atan2(xy.y, xy.x); + if (xy.x >= 0.0 && xy.x >= fabs(xy.y)) { + area = AREA_0; + } else if (xy.y >= 0.0 && xy.y >= fabs(xy.x)) { + area = AREA_1; + mu -= M_HALFPI; + } else if (xy.x < 0.0 && -xy.x >= fabs(xy.y)) { + area = AREA_2; + mu = (mu < 0.0 ? mu + M_PI : mu - M_PI); + } else { + area = AREA_3; + mu += M_HALFPI; + } + + /* Compute phi and theta for the area of definition. + * The inverse projection is not described in the original paper, but some + * good hints can be found here (as of 2011-12-14): + * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302 + * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */ + t = (M_PI / 12.0) * tan(mu); + tantheta = sin(t) / (cos(t) - (1.0 / sqrt(2.0))); + theta = atan(tantheta); + cosmu = cos(mu); + tannu = tan(nu); + cosphi = 1.0 - cosmu * cosmu * tannu * tannu * (1.0 - cos(atan(1.0 / cos(theta)))); + if (cosphi < -1.0) { + cosphi = -1.0; + } else if (cosphi > +1.0) { + cosphi = +1.0; + } + + /* Apply the result to the real area on the cube face. + * For the top and bottom face, we can compute phi and lam directly. + * For the other faces, we must use unit sphere cartesian coordinates + * as an intermediate step. */ + if (Q->face == FACE_TOP) { + phi = acos(cosphi); + lp.phi = M_HALFPI - phi; + if (area == AREA_0) { + lp.lam = theta + M_HALFPI; + } else if (area == AREA_1) { + lp.lam = (theta < 0.0 ? theta + M_PI : theta - M_PI); + } else if (area == AREA_2) { + lp.lam = theta - M_HALFPI; + } else /* area == AREA_3 */ { + lp.lam = theta; + } + } else if (Q->face == FACE_BOTTOM) { + phi = acos(cosphi); + lp.phi = phi - M_HALFPI; + if (area == AREA_0) { + lp.lam = -theta + M_HALFPI; + } else if (area == AREA_1) { + lp.lam = -theta; + } else if (area == AREA_2) { + lp.lam = -theta - M_HALFPI; + } else /* area == AREA_3 */ { + lp.lam = (theta < 0.0 ? -theta - M_PI : -theta + M_PI); + } + } else { + /* Compute phi and lam via cartesian unit sphere coordinates. */ + double q, r, s; + q = cosphi; + t = q * q; + if (t >= 1.0) { + s = 0.0; + } else { + s = sqrt(1.0 - t) * sin(theta); + } + t += s * s; + if (t >= 1.0) { + r = 0.0; + } else { + r = sqrt(1.0 - t); + } + /* Rotate q,r,s into the correct area. */ + if (area == AREA_1) { + t = r; + r = -s; + s = t; + } else if (area == AREA_2) { + r = -r; + s = -s; + } else if (area == AREA_3) { + t = r; + r = s; + s = -t; + } + /* Rotate q,r,s into the correct cube face. */ + if (Q->face == FACE_RIGHT) { + t = q; + q = -r; + r = t; + } else if (Q->face == FACE_BACK) { + q = -q; + r = -r; + } else if (Q->face == FACE_LEFT) { + t = q; + q = r; + r = -t; + } + /* Now compute phi and lam from the unit sphere coordinates. */ + lp.phi = acos(-s) - M_HALFPI; + lp.lam = atan2(r, q); + if (Q->face == FACE_RIGHT) { + lp.lam = qsc_shift_lon_origin(lp.lam, -M_HALFPI); + } else if (Q->face == FACE_BACK) { + lp.lam = qsc_shift_lon_origin(lp.lam, -M_PI); + } else if (Q->face == FACE_LEFT) { + lp.lam = qsc_shift_lon_origin(lp.lam, +M_HALFPI); + } + } + + /* Apply the shift from the sphere to the ellipsoid as described + * in [LK12]. */ + if (P->es != 0.0) { + int invert_sign; + double tanphi, xa; + invert_sign = (lp.phi < 0.0 ? 1 : 0); + tanphi = tan(lp.phi); + xa = Q->b / sqrt(tanphi * tanphi + Q->one_minus_f_squared); + lp.phi = atan(sqrt(P->a * P->a - xa * xa) / (Q->one_minus_f * xa)); + if (invert_sign) { + lp.phi = -lp.phi; + } + } + return lp; +} + + +PJ *PROJECTION(qsc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + P->inv = e_inverse; + P->fwd = e_forward; + /* Determine the cube face from the center of projection. */ + if (P->phi0 >= M_HALFPI - M_FORTPI / 2.0) { + Q->face = FACE_TOP; + } else if (P->phi0 <= -(M_HALFPI - M_FORTPI / 2.0)) { + Q->face = FACE_BOTTOM; + } else if (fabs(P->lam0) <= M_FORTPI) { + Q->face = FACE_FRONT; + } else if (fabs(P->lam0) <= M_HALFPI + M_FORTPI) { + Q->face = (P->lam0 > 0.0 ? FACE_RIGHT : FACE_LEFT); + } else { + Q->face = FACE_BACK; + } + /* Fill in useful values for the ellipsoid <-> sphere shift + * described in [LK12]. */ + if (P->es != 0.0) { + Q->a_squared = P->a * P->a; + Q->b = P->a * sqrt(1.0 - P->es); + Q->one_minus_f = 1.0 - (P->a - Q->b) / P->a; + Q->one_minus_f_squared = Q->one_minus_f * Q->one_minus_f; + } + + return P; +} diff --git a/src/PJ_robin.c b/src/PJ_robin.c deleted file mode 100644 index 19bdc2dc..00000000 --- a/src/PJ_robin.c +++ /dev/null @@ -1,159 +0,0 @@ -#define PJ_LIB__ -#include "proj_math.h" -#include "proj_internal.h" -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(robin, "Robinson") "\n\tPCyl, Sph"; - -#define V(C,z) (C.c0 + z * (C.c1 + z * (C.c2 + z * C.c3))) -#define DV(C,z) (C.c1 + z * (C.c2 + C.c2 + z * 3. * C.c3)) - -/* -note: following terms based upon 5 deg. intervals in degrees. - -Some background on these coefficients is available at: - -http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039 -http://trac.osgeo.org/proj/ticket/113 -*/ - -struct COEFS { - float c0, c1, c2, c3; -}; - -static const struct COEFS X[] = { - {1.0f, 2.2199e-17f, -7.15515e-05f, 3.1103e-06f}, - {0.9986f, -0.000482243f, -2.4897e-05f, -1.3309e-06f}, - {0.9954f, -0.00083103f, -4.48605e-05f, -9.86701e-07f}, - {0.99f, -0.00135364f, -5.9661e-05f, 3.6777e-06f}, - {0.9822f, -0.00167442f, -4.49547e-06f, -5.72411e-06f}, - {0.973f, -0.00214868f, -9.03571e-05f, 1.8736e-08f}, - {0.96f, -0.00305085f, -9.00761e-05f, 1.64917e-06f}, - {0.9427f, -0.00382792f, -6.53386e-05f, -2.6154e-06f}, - {0.9216f, -0.00467746f, -0.00010457f, 4.81243e-06f}, - {0.8962f, -0.00536223f, -3.23831e-05f, -5.43432e-06f}, - {0.8679f, -0.00609363f, -0.000113898f, 3.32484e-06f}, - {0.835f, -0.00698325f, -6.40253e-05f, 9.34959e-07f}, - {0.7986f, -0.00755338f, -5.00009e-05f, 9.35324e-07f}, - {0.7597f, -0.00798324f, -3.5971e-05f, -2.27626e-06f}, - {0.7186f, -0.00851367f, -7.01149e-05f, -8.6303e-06f}, - {0.6732f, -0.00986209f, -0.000199569f, 1.91974e-05f}, - {0.6213f, -0.010418f, 8.83923e-05f, 6.24051e-06f}, - {0.5722f, -0.00906601f, 0.000182f, 6.24051e-06f}, - {0.5322f, -0.00677797f, 0.000275608f, 6.24051e-06f} -}; - -static const struct COEFS Y[] = { - {-5.20417e-18f, 0.0124f, 1.21431e-18f, -8.45284e-11f}, - {0.062f, 0.0124f, -1.26793e-09f, 4.22642e-10f}, - {0.124f, 0.0124f, 5.07171e-09f, -1.60604e-09f}, - {0.186f, 0.0123999f, -1.90189e-08f, 6.00152e-09f}, - {0.248f, 0.0124002f, 7.10039e-08f, -2.24e-08f}, - {0.31f, 0.0123992f, -2.64997e-07f, 8.35986e-08f}, - {0.372f, 0.0124029f, 9.88983e-07f, -3.11994e-07f}, - {0.434f, 0.0123893f, -3.69093e-06f, -4.35621e-07f}, - {0.4958f, 0.0123198f, -1.02252e-05f, -3.45523e-07f}, - {0.5571f, 0.0121916f, -1.54081e-05f, -5.82288e-07f}, - {0.6176f, 0.0119938f, -2.41424e-05f, -5.25327e-07f}, - {0.6769f, 0.011713f, -3.20223e-05f, -5.16405e-07f}, - {0.7346f, 0.0113541f, -3.97684e-05f, -6.09052e-07f}, - {0.7903f, 0.0109107f, -4.89042e-05f, -1.04739e-06f}, - {0.8435f, 0.0103431f, -6.4615e-05f, -1.40374e-09f}, - {0.8936f, 0.00969686f, -6.4636e-05f, -8.547e-06f}, - {0.9394f, 0.00840947f, -0.000192841f, -4.2106e-06f}, - {0.9761f, 0.00616527f, -0.000256f, -4.2106e-06f}, - {1.0f, 0.00328947f, -0.000319159f, -4.2106e-06f} -}; - -#define FXC 0.8487 -#define FYC 1.3523 -#define C1 11.45915590261646417544 -#define RC1 0.08726646259971647884 -#define NODES 18 -#define ONEEPS 1.000001 -#define EPS 1e-8 -/* Not sure at all of the appropriate number for MAX_ITER... */ -#define MAX_ITER 100 - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - long i; - double dphi; - (void) P; - - dphi = fabs(lp.phi); - i = isnan(lp.phi) ? -1 : lround(floor(dphi * C1)); - if( i < 0 ){ - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - if (i >= NODES) i = NODES - 1; - dphi = RAD_TO_DEG * (dphi - RC1 * i); - xy.x = V(X[i], dphi) * FXC * lp.lam; - xy.y = V(Y[i], dphi) * FYC; - if (lp.phi < 0.) xy.y = -xy.y; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - long i; - double t, t1; - struct COEFS T; - int iters; - - lp.lam = xy.x / FXC; - lp.phi = fabs(xy.y / FYC); - if (lp.phi >= 1.) { /* simple pathologic cases */ - if (lp.phi > ONEEPS) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - else { - lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; - lp.lam /= X[NODES].c0; - } - } else { /* general problem */ - /* in Y space, reduce to table interval */ - i = isnan(lp.phi) ? -1 : lround(floor(lp.phi * NODES)); - if( i < 0 || i >= NODES ) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - for (;;) { - if (Y[i].c0 > lp.phi) --i; - else if (Y[i+1].c0 <= lp.phi) ++i; - else break; - } - T = Y[i]; - /* first guess, linear interp */ - t = 5. * (lp.phi - T.c0)/(Y[i+1].c0 - T.c0); - /* make into root */ - T.c0 = (float)(T.c0 - lp.phi); - for (iters = MAX_ITER; iters ; --iters) { /* Newton-Raphson */ - t -= t1 = V(T,t) / DV(T,t); - if (fabs(t1) < EPS) - break; - } - if( iters == 0 ) - pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); - lp.phi = (5 * i + t) * DEG_TO_RAD; - if (xy.y < 0.) lp.phi = -lp.phi; - lp.lam /= V(X[i], t); - } - return lp; -} - - -PJ *PROJECTION(robin) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - - diff --git a/src/PJ_robin.cpp b/src/PJ_robin.cpp new file mode 100644 index 00000000..19bdc2dc --- /dev/null +++ b/src/PJ_robin.cpp @@ -0,0 +1,159 @@ +#define PJ_LIB__ +#include "proj_math.h" +#include "proj_internal.h" +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(robin, "Robinson") "\n\tPCyl, Sph"; + +#define V(C,z) (C.c0 + z * (C.c1 + z * (C.c2 + z * C.c3))) +#define DV(C,z) (C.c1 + z * (C.c2 + C.c2 + z * 3. * C.c3)) + +/* +note: following terms based upon 5 deg. intervals in degrees. + +Some background on these coefficients is available at: + +http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039 +http://trac.osgeo.org/proj/ticket/113 +*/ + +struct COEFS { + float c0, c1, c2, c3; +}; + +static const struct COEFS X[] = { + {1.0f, 2.2199e-17f, -7.15515e-05f, 3.1103e-06f}, + {0.9986f, -0.000482243f, -2.4897e-05f, -1.3309e-06f}, + {0.9954f, -0.00083103f, -4.48605e-05f, -9.86701e-07f}, + {0.99f, -0.00135364f, -5.9661e-05f, 3.6777e-06f}, + {0.9822f, -0.00167442f, -4.49547e-06f, -5.72411e-06f}, + {0.973f, -0.00214868f, -9.03571e-05f, 1.8736e-08f}, + {0.96f, -0.00305085f, -9.00761e-05f, 1.64917e-06f}, + {0.9427f, -0.00382792f, -6.53386e-05f, -2.6154e-06f}, + {0.9216f, -0.00467746f, -0.00010457f, 4.81243e-06f}, + {0.8962f, -0.00536223f, -3.23831e-05f, -5.43432e-06f}, + {0.8679f, -0.00609363f, -0.000113898f, 3.32484e-06f}, + {0.835f, -0.00698325f, -6.40253e-05f, 9.34959e-07f}, + {0.7986f, -0.00755338f, -5.00009e-05f, 9.35324e-07f}, + {0.7597f, -0.00798324f, -3.5971e-05f, -2.27626e-06f}, + {0.7186f, -0.00851367f, -7.01149e-05f, -8.6303e-06f}, + {0.6732f, -0.00986209f, -0.000199569f, 1.91974e-05f}, + {0.6213f, -0.010418f, 8.83923e-05f, 6.24051e-06f}, + {0.5722f, -0.00906601f, 0.000182f, 6.24051e-06f}, + {0.5322f, -0.00677797f, 0.000275608f, 6.24051e-06f} +}; + +static const struct COEFS Y[] = { + {-5.20417e-18f, 0.0124f, 1.21431e-18f, -8.45284e-11f}, + {0.062f, 0.0124f, -1.26793e-09f, 4.22642e-10f}, + {0.124f, 0.0124f, 5.07171e-09f, -1.60604e-09f}, + {0.186f, 0.0123999f, -1.90189e-08f, 6.00152e-09f}, + {0.248f, 0.0124002f, 7.10039e-08f, -2.24e-08f}, + {0.31f, 0.0123992f, -2.64997e-07f, 8.35986e-08f}, + {0.372f, 0.0124029f, 9.88983e-07f, -3.11994e-07f}, + {0.434f, 0.0123893f, -3.69093e-06f, -4.35621e-07f}, + {0.4958f, 0.0123198f, -1.02252e-05f, -3.45523e-07f}, + {0.5571f, 0.0121916f, -1.54081e-05f, -5.82288e-07f}, + {0.6176f, 0.0119938f, -2.41424e-05f, -5.25327e-07f}, + {0.6769f, 0.011713f, -3.20223e-05f, -5.16405e-07f}, + {0.7346f, 0.0113541f, -3.97684e-05f, -6.09052e-07f}, + {0.7903f, 0.0109107f, -4.89042e-05f, -1.04739e-06f}, + {0.8435f, 0.0103431f, -6.4615e-05f, -1.40374e-09f}, + {0.8936f, 0.00969686f, -6.4636e-05f, -8.547e-06f}, + {0.9394f, 0.00840947f, -0.000192841f, -4.2106e-06f}, + {0.9761f, 0.00616527f, -0.000256f, -4.2106e-06f}, + {1.0f, 0.00328947f, -0.000319159f, -4.2106e-06f} +}; + +#define FXC 0.8487 +#define FYC 1.3523 +#define C1 11.45915590261646417544 +#define RC1 0.08726646259971647884 +#define NODES 18 +#define ONEEPS 1.000001 +#define EPS 1e-8 +/* Not sure at all of the appropriate number for MAX_ITER... */ +#define MAX_ITER 100 + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + long i; + double dphi; + (void) P; + + dphi = fabs(lp.phi); + i = isnan(lp.phi) ? -1 : lround(floor(dphi * C1)); + if( i < 0 ){ + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + if (i >= NODES) i = NODES - 1; + dphi = RAD_TO_DEG * (dphi - RC1 * i); + xy.x = V(X[i], dphi) * FXC * lp.lam; + xy.y = V(Y[i], dphi) * FYC; + if (lp.phi < 0.) xy.y = -xy.y; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + long i; + double t, t1; + struct COEFS T; + int iters; + + lp.lam = xy.x / FXC; + lp.phi = fabs(xy.y / FYC); + if (lp.phi >= 1.) { /* simple pathologic cases */ + if (lp.phi > ONEEPS) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + else { + lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; + lp.lam /= X[NODES].c0; + } + } else { /* general problem */ + /* in Y space, reduce to table interval */ + i = isnan(lp.phi) ? -1 : lround(floor(lp.phi * NODES)); + if( i < 0 || i >= NODES ) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + for (;;) { + if (Y[i].c0 > lp.phi) --i; + else if (Y[i+1].c0 <= lp.phi) ++i; + else break; + } + T = Y[i]; + /* first guess, linear interp */ + t = 5. * (lp.phi - T.c0)/(Y[i+1].c0 - T.c0); + /* make into root */ + T.c0 = (float)(T.c0 - lp.phi); + for (iters = MAX_ITER; iters ; --iters) { /* Newton-Raphson */ + t -= t1 = V(T,t) / DV(T,t); + if (fabs(t1) < EPS) + break; + } + if( iters == 0 ) + pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT ); + lp.phi = (5 * i + t) * DEG_TO_RAD; + if (xy.y < 0.) lp.phi = -lp.phi; + lp.lam /= V(X[i], t); + } + return lp; +} + + +PJ *PROJECTION(robin) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + diff --git a/src/PJ_rpoly.c b/src/PJ_rpoly.c deleted file mode 100644 index 03c2b161..00000000 --- a/src/PJ_rpoly.c +++ /dev/null @@ -1,56 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -struct pj_opaque { - double phi1; - double fxa; - double fxb; - int mode; -}; - -PROJ_HEAD(rpoly, "Rectangular Polyconic") - "\n\tConic, Sph, no inv\n\tlat_ts="; - -#define EPS 1e-9 - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double fa; - - if (Q->mode) - fa = tan(lp.lam * Q->fxb) * Q->fxa; - else - fa = 0.5 * lp.lam; - if (fabs(lp.phi) < EPS) { - xy.x = fa + fa; - xy.y = - P->phi0; - } else { - xy.y = 1. / tan(lp.phi); - xy.x = sin(fa = 2. * atan(fa * sin(lp.phi))) * xy.y; - xy.y = lp.phi - P->phi0 + (1. - cos(fa)) * xy.y; - } - return xy; -} - - - -PJ *PROJECTION(rpoly) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - if ((Q->mode = (Q->phi1 = fabs(pj_param(P->ctx, P->params, "rlat_ts").f)) > EPS)) { - Q->fxb = 0.5 * sin(Q->phi1); - Q->fxa = 0.5 / Q->fxb; - } - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_rpoly.cpp b/src/PJ_rpoly.cpp new file mode 100644 index 00000000..24360965 --- /dev/null +++ b/src/PJ_rpoly.cpp @@ -0,0 +1,56 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +struct pj_opaque { + double phi1; + double fxa; + double fxb; + int mode; +}; + +PROJ_HEAD(rpoly, "Rectangular Polyconic") + "\n\tConic, Sph, no inv\n\tlat_ts="; + +#define EPS 1e-9 + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double fa; + + if (Q->mode) + fa = tan(lp.lam * Q->fxb) * Q->fxa; + else + fa = 0.5 * lp.lam; + if (fabs(lp.phi) < EPS) { + xy.x = fa + fa; + xy.y = - P->phi0; + } else { + xy.y = 1. / tan(lp.phi); + xy.x = sin(fa = 2. * atan(fa * sin(lp.phi))) * xy.y; + xy.y = lp.phi - P->phi0 + (1. - cos(fa)) * xy.y; + } + return xy; +} + + + +PJ *PROJECTION(rpoly) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + if ((Q->mode = (Q->phi1 = fabs(pj_param(P->ctx, P->params, "rlat_ts").f)) > EPS)) { + Q->fxb = 0.5 * sin(Q->phi1); + Q->fxa = 0.5 / Q->fxb; + } + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_sch.c b/src/PJ_sch.c deleted file mode 100644 index 64e5cdb8..00000000 --- a/src/PJ_sch.c +++ /dev/null @@ -1,230 +0,0 @@ -/****************************************************************************** - * Project: SCH Coordinate system - * Purpose: Implementation of SCH Coordinate system - * References : - * 1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000. - * 2. Buckley, Sean Monroe. Radar interferometry measurement of land subsidence. 2000.. - * PhD Thesis. UT Austin. (Appendix) - * 3. Hensley, Scott, Elaine Chapin, and T. Michel. "Improved processing of AIRSAR - * data based on the GeoSAR processor." Airsar earth science and applications - * workshop, March. 2002. (http://airsar.jpl.nasa.gov/documents/workshop2002/papers/T3.pdf) - * - * Author: Piyush Agram (piyush.agram@jpl.nasa.gov) - * Copyright (c) 2015 California Institute of Technology. - * Government sponsorship acknowledged. - * - * NOTE: The SCH coordinate system is a sensor aligned coordinate system - * developed at JPL for radar mapping missions. Details pertaining to the - * coordinate system have been release in the public domain (see references above). - * This code is an independent implementation of the SCH coordinate system - * that conforms to the PROJ.4 conventions and uses the details presented in these - * publicly released documents. All credit for the development of the coordinate - * system and its use should be directed towards the original developers at JPL. - ****************************************************************************** - * 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. - ****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" -#include "geocent.h" - -struct pj_opaque { - double plat; /*Peg Latitude */ - double plon; /*Peg Longitude*/ - double phdg; /*Peg heading */ - double h0; /*Average altitude */ - double transMat[9]; - double xyzoff[3]; - double rcurv; - GeocentricInfo sph; - GeocentricInfo elp_0; -}; - -PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0= plon_0= phdg_0= [h_0=]"; - -static LPZ inverse3d(XYZ xyz, PJ *P) { - LPZ lpz = {0.0, 0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double temp[3]; - double pxyz[3]; - - /* Local lat,lon using radius */ - pxyz[0] = xyz.y * P->a / Q->rcurv; - pxyz[1] = xyz.x * P->a / Q->rcurv; - pxyz[2] = xyz.z; - - if( pj_Convert_Geodetic_To_Geocentric( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], temp, temp+1, temp+2) != 0) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lpz; - } - - /* Apply rotation */ - pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[1] * temp[1] + Q->transMat[2] * temp[2]; - pxyz[1] = Q->transMat[3] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[5] * temp[2]; - pxyz[2] = Q->transMat[6] * temp[0] + Q->transMat[7] * temp[1] + Q->transMat[8] * temp[2]; - - /* Apply offset */ - pxyz[0] += Q->xyzoff[0]; - pxyz[1] += Q->xyzoff[1]; - pxyz[2] += Q->xyzoff[2]; - - /* Convert geocentric coordinates to lat lon */ - pj_Convert_Geocentric_To_Geodetic( &(Q->elp_0), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2); - - - lpz.lam = temp[1] ; - lpz.phi = temp[0] ; - lpz.z = temp[2]; - - return lpz; -} - -static XYZ forward3d(LPZ lpz, PJ *P) { - XYZ xyz = {0.0, 0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double temp[3]; - double pxyz[3]; - - - /* Convert lat lon to geocentric coordinates */ - if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), lpz.phi, lpz.lam, lpz.z, temp, temp+1, temp+2 ) != 0 ) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xyz; - } - - - /* Adjust for offset */ - temp[0] -= Q->xyzoff[0]; - temp[1] -= Q->xyzoff[1]; - temp[2] -= Q->xyzoff[2]; - - - /* Apply rotation */ - pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[3] * temp[1] + Q->transMat[6] * temp[2]; - pxyz[1] = Q->transMat[1] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[7] * temp[2]; - pxyz[2] = Q->transMat[2] * temp[0] + Q->transMat[5] * temp[1] + Q->transMat[8] * temp[2]; - - /* Convert to local lat,lon */ - pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2); - - - /* Scale by radius */ - xyz.x = temp[1] * Q->rcurv / P->a; - xyz.y = temp[0] * Q->rcurv / P->a; - xyz.z = temp[2]; - - return xyz; -} - - -static PJ *setup(PJ *P) { /* general initialization */ - struct pj_opaque *Q = P->opaque; - double reast, rnorth; - double chdg, shdg; - double clt, slt; - double clo, slo; - double temp; - double pxyz[3]; - - temp = P->a * sqrt(1.0 - P->es); - - /* Setup original geocentric system */ - if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0) - return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - - clt = cos(Q->plat); - slt = sin(Q->plat); - clo = cos(Q->plon); - slo = sin(Q->plon); - - /* Estimate the radius of curvature for given peg */ - temp = sqrt(1.0 - (P->es) * slt * slt); - reast = (P->a)/temp; - rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3); - - chdg = cos(Q->phdg); - shdg = sin(Q->phdg); - - Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); - - /* Set up local sphere at the given peg point */ - if ( pj_Set_Geocentric_Parameters(&(Q->sph), Q->rcurv, Q->rcurv) != 0) - return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - - /* Set up the transformation matrices */ - Q->transMat[0] = clt * clo; - Q->transMat[1] = -shdg*slo - slt*clo * chdg; - Q->transMat[2] = slo*chdg - slt*clo*shdg; - Q->transMat[3] = clt*slo; - Q->transMat[4] = clo*shdg - slt*slo*chdg; - Q->transMat[5] = -clo*chdg - slt*slo*shdg; - Q->transMat[6] = slt; - Q->transMat[7] = clt*chdg; - Q->transMat[8] = clt*shdg; - - - if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0, - pxyz, pxyz+1, pxyz+2 ) != 0 ) - return pj_default_destructor(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - - - Q->xyzoff[0] = pxyz[0] - (Q->rcurv) * clt * clo; - Q->xyzoff[1] = pxyz[1] - (Q->rcurv) * clt * slo; - Q->xyzoff[2] = pxyz[2] - (Q->rcurv) * slt; - - P->fwd3d = forward3d; - P->inv3d = inverse3d; - return P; -} - - -PJ *PROJECTION(sch) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - Q->h0 = 0.0; - - /* Check if peg latitude was defined */ - if (pj_param(P->ctx, P->params, "tplat_0").i) - Q->plat = pj_param(P->ctx, P->params, "rplat_0").f; - else { - return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - } - - /* Check if peg longitude was defined */ - if (pj_param(P->ctx, P->params, "tplon_0").i) - Q->plon = pj_param(P->ctx, P->params, "rplon_0").f; - else { - return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - } - - /* Check if peg latitude is defined */ - if (pj_param(P->ctx, P->params, "tphdg_0").i) - Q->phdg = pj_param(P->ctx, P->params, "rphdg_0").f; - else { - return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); - } - - - /* Check if average height was defined - If so read it in */ - if (pj_param(P->ctx, P->params, "th_0").i) - Q->h0 = pj_param(P->ctx, P->params, "dh_0").f; - - - return setup(P); -} diff --git a/src/PJ_sch.cpp b/src/PJ_sch.cpp new file mode 100644 index 00000000..4a0b1fb8 --- /dev/null +++ b/src/PJ_sch.cpp @@ -0,0 +1,230 @@ +/****************************************************************************** + * Project: SCH Coordinate system + * Purpose: Implementation of SCH Coordinate system + * References : + * 1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000. + * 2. Buckley, Sean Monroe. Radar interferometry measurement of land subsidence. 2000.. + * PhD Thesis. UT Austin. (Appendix) + * 3. Hensley, Scott, Elaine Chapin, and T. Michel. "Improved processing of AIRSAR + * data based on the GeoSAR processor." Airsar earth science and applications + * workshop, March. 2002. (http://airsar.jpl.nasa.gov/documents/workshop2002/papers/T3.pdf) + * + * Author: Piyush Agram (piyush.agram@jpl.nasa.gov) + * Copyright (c) 2015 California Institute of Technology. + * Government sponsorship acknowledged. + * + * NOTE: The SCH coordinate system is a sensor aligned coordinate system + * developed at JPL for radar mapping missions. Details pertaining to the + * coordinate system have been release in the public domain (see references above). + * This code is an independent implementation of the SCH coordinate system + * that conforms to the PROJ.4 conventions and uses the details presented in these + * publicly released documents. All credit for the development of the coordinate + * system and its use should be directed towards the original developers at JPL. + ****************************************************************************** + * 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. + ****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" +#include "geocent.h" + +struct pj_opaque { + double plat; /*Peg Latitude */ + double plon; /*Peg Longitude*/ + double phdg; /*Peg heading */ + double h0; /*Average altitude */ + double transMat[9]; + double xyzoff[3]; + double rcurv; + GeocentricInfo sph; + GeocentricInfo elp_0; +}; + +PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0= plon_0= phdg_0= [h_0=]"; + +static LPZ inverse3d(XYZ xyz, PJ *P) { + LPZ lpz = {0.0, 0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double temp[3]; + double pxyz[3]; + + /* Local lat,lon using radius */ + pxyz[0] = xyz.y * P->a / Q->rcurv; + pxyz[1] = xyz.x * P->a / Q->rcurv; + pxyz[2] = xyz.z; + + if( pj_Convert_Geodetic_To_Geocentric( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], temp, temp+1, temp+2) != 0) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lpz; + } + + /* Apply rotation */ + pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[1] * temp[1] + Q->transMat[2] * temp[2]; + pxyz[1] = Q->transMat[3] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[5] * temp[2]; + pxyz[2] = Q->transMat[6] * temp[0] + Q->transMat[7] * temp[1] + Q->transMat[8] * temp[2]; + + /* Apply offset */ + pxyz[0] += Q->xyzoff[0]; + pxyz[1] += Q->xyzoff[1]; + pxyz[2] += Q->xyzoff[2]; + + /* Convert geocentric coordinates to lat lon */ + pj_Convert_Geocentric_To_Geodetic( &(Q->elp_0), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+2); + + + lpz.lam = temp[1] ; + lpz.phi = temp[0] ; + lpz.z = temp[2]; + + return lpz; +} + +static XYZ forward3d(LPZ lpz, PJ *P) { + XYZ xyz = {0.0, 0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double temp[3]; + double pxyz[3]; + + + /* Convert lat lon to geocentric coordinates */ + if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), lpz.phi, lpz.lam, lpz.z, temp, temp+1, temp+2 ) != 0 ) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xyz; + } + + + /* Adjust for offset */ + temp[0] -= Q->xyzoff[0]; + temp[1] -= Q->xyzoff[1]; + temp[2] -= Q->xyzoff[2]; + + + /* Apply rotation */ + pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[3] * temp[1] + Q->transMat[6] * temp[2]; + pxyz[1] = Q->transMat[1] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[7] * temp[2]; + pxyz[2] = Q->transMat[2] * temp[0] + Q->transMat[5] * temp[1] + Q->transMat[8] * temp[2]; + + /* Convert to local lat,lon */ + pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+2); + + + /* Scale by radius */ + xyz.x = temp[1] * Q->rcurv / P->a; + xyz.y = temp[0] * Q->rcurv / P->a; + xyz.z = temp[2]; + + return xyz; +} + + +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = static_cast(P->opaque); + double reast, rnorth; + double chdg, shdg; + double clt, slt; + double clo, slo; + double temp; + double pxyz[3]; + + temp = P->a * sqrt(1.0 - P->es); + + /* Setup original geocentric system */ + if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0) + return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + + clt = cos(Q->plat); + slt = sin(Q->plat); + clo = cos(Q->plon); + slo = sin(Q->plon); + + /* Estimate the radius of curvature for given peg */ + temp = sqrt(1.0 - (P->es) * slt * slt); + reast = (P->a)/temp; + rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3); + + chdg = cos(Q->phdg); + shdg = sin(Q->phdg); + + Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); + + /* Set up local sphere at the given peg point */ + if ( pj_Set_Geocentric_Parameters(&(Q->sph), Q->rcurv, Q->rcurv) != 0) + return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + + /* Set up the transformation matrices */ + Q->transMat[0] = clt * clo; + Q->transMat[1] = -shdg*slo - slt*clo * chdg; + Q->transMat[2] = slo*chdg - slt*clo*shdg; + Q->transMat[3] = clt*slo; + Q->transMat[4] = clo*shdg - slt*slo*chdg; + Q->transMat[5] = -clo*chdg - slt*slo*shdg; + Q->transMat[6] = slt; + Q->transMat[7] = clt*chdg; + Q->transMat[8] = clt*shdg; + + + if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0, + pxyz, pxyz+1, pxyz+2 ) != 0 ) + return pj_default_destructor(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + + + Q->xyzoff[0] = pxyz[0] - (Q->rcurv) * clt * clo; + Q->xyzoff[1] = pxyz[1] - (Q->rcurv) * clt * slo; + Q->xyzoff[2] = pxyz[2] - (Q->rcurv) * slt; + + P->fwd3d = forward3d; + P->inv3d = inverse3d; + return P; +} + + +PJ *PROJECTION(sch) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + Q->h0 = 0.0; + + /* Check if peg latitude was defined */ + if (pj_param(P->ctx, P->params, "tplat_0").i) + Q->plat = pj_param(P->ctx, P->params, "rplat_0").f; + else { + return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + } + + /* Check if peg longitude was defined */ + if (pj_param(P->ctx, P->params, "tplon_0").i) + Q->plon = pj_param(P->ctx, P->params, "rplon_0").f; + else { + return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + } + + /* Check if peg latitude is defined */ + if (pj_param(P->ctx, P->params, "tphdg_0").i) + Q->phdg = pj_param(P->ctx, P->params, "rphdg_0").f; + else { + return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ); + } + + + /* Check if average height was defined - If so read it in */ + if (pj_param(P->ctx, P->params, "th_0").i) + Q->h0 = pj_param(P->ctx, P->params, "dh_0").f; + + + return setup(P); +} diff --git a/src/PJ_sconics.c b/src/PJ_sconics.c deleted file mode 100644 index ce044c24..00000000 --- a/src/PJ_sconics.c +++ /dev/null @@ -1,216 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - - -enum Type { - EULER = 0, - MURD1 = 1, - MURD2 = 2, - MURD3 = 3, - PCONIC = 4, - TISSOT = 5, - VITK1 = 6 -}; - -struct pj_opaque { - double n; - double rho_c; - double rho_0; - double sig; - double c1, c2; - enum Type type; -}; - - -#define EPS10 1.e-10 -#define EPS 1e-10 -#define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2=" - -PROJ_HEAD(euler, "Euler") LINE2; -PROJ_HEAD(murd1, "Murdoch I") LINE2; -PROJ_HEAD(murd2, "Murdoch II") LINE2; -PROJ_HEAD(murd3, "Murdoch III") LINE2; -PROJ_HEAD(pconic, "Perspective Conic") LINE2; -PROJ_HEAD(tissot, "Tissot") LINE2; -PROJ_HEAD(vitk1, "Vitkovsky I") LINE2; - - - -/* get common factors for simple conics */ -static int phi12(PJ *P, double *del) { - double p1, p2; - int err = 0; - - if (!pj_param(P->ctx, P->params, "tlat_1").i || - !pj_param(P->ctx, P->params, "tlat_2").i) { - err = -41; - } else { - p1 = pj_param(P->ctx, P->params, "rlat_1").f; - p2 = pj_param(P->ctx, P->params, "rlat_2").f; - *del = 0.5 * (p2 - p1); - P->opaque->sig = 0.5 * (p2 + p1); - err = (fabs(*del) < EPS || fabs(P->opaque->sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0; - } - return err; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double rho; - - switch (Q->type) { - case MURD2: - rho = Q->rho_c + tan (Q->sig - lp.phi); - break; - case PCONIC: - rho = Q->c2 * (Q->c1 - tan (lp.phi - Q->sig)); - break; - default: - rho = Q->rho_c - lp.phi; - break; - } - - xy.x = rho * sin ( lp.lam *= Q->n ); - xy.y = Q->rho_0 - rho * cos (lp.lam); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */ - LP lp = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double rho; - - rho = hypot (xy.x, xy.y = Q->rho_0 - xy.y); - if (Q->n < 0.) { - rho = - rho; - xy.x = - xy.x; - xy.y = - xy.y; - } - - lp.lam = atan2 (xy.x, xy.y) / Q->n; - - switch (Q->type) { - case PCONIC: - lp.phi = atan (Q->c1 - rho / Q->c2) + Q->sig; - break; - case MURD2: - lp.phi = Q->sig - atan(rho - Q->rho_c); - break; - default: - lp.phi = Q->rho_c - rho; - } - return lp; -} - - -static PJ *setup(PJ *P, enum Type type) { - double del, cs; - int err; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - Q->type = type; - - err = phi12 (P, &del); - if(err) - return pj_default_destructor (P, err); - - switch (Q->type) { - - case TISSOT: - Q->n = sin (Q->sig); - cs = cos (del); - Q->rho_c = Q->n / cs + cs / Q->n; - Q->rho_0 = sqrt ((Q->rho_c - 2 * sin (P->phi0)) / Q->n); - break; - - case MURD1: - Q->rho_c = sin(del)/(del * tan(Q->sig)) + Q->sig; - Q->rho_0 = Q->rho_c - P->phi0; - Q->n = sin(Q->sig); - break; - - case MURD2: - Q->rho_c = (cs = sqrt (cos (del))) / tan (Q->sig); - Q->rho_0 = Q->rho_c + tan (Q->sig - P->phi0); - Q->n = sin (Q->sig) * cs; - break; - - case MURD3: - Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig; - Q->rho_0 = Q->rho_c - P->phi0; - Q->n = sin (Q->sig) * sin (del) * tan (del) / (del * del); - break; - - case EULER: - Q->n = sin (Q->sig) * sin (del) / del; - del *= 0.5; - Q->rho_c = del / (tan (del) * tan (Q->sig)) + Q->sig; - Q->rho_0 = Q->rho_c - P->phi0; - break; - - case PCONIC: - Q->n = sin (Q->sig); - Q->c2 = cos (del); - Q->c1 = 1./tan (Q->sig); - if (fabs (del = P->phi0 - Q->sig) - EPS10 >= M_HALFPI) - return pj_default_destructor(P, PJD_ERR_LAT_0_HALF_PI_FROM_MEAN); - - Q->rho_0 = Q->c2 * (Q->c1 - tan (del)); - break; - - case VITK1: - Q->n = (cs = tan (del)) * sin (Q->sig) / del; - Q->rho_c = del / (cs * tan (Q->sig)) + Q->sig; - Q->rho_0 = Q->rho_c - P->phi0; - break; - } - - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0; - return (P); -} - - -PJ *PROJECTION(euler) { - return setup(P, EULER); -} - - -PJ *PROJECTION(tissot) { - return setup(P, TISSOT); -} - - -PJ *PROJECTION(murd1) { - return setup(P, MURD1); -} - - -PJ *PROJECTION(murd2) { - return setup(P, MURD2); -} - - -PJ *PROJECTION(murd3) { - return setup(P, MURD3); -} - - -PJ *PROJECTION(pconic) { - return setup(P, PCONIC); -} - - -PJ *PROJECTION(vitk1) { - return setup(P, VITK1); -} - diff --git a/src/PJ_sconics.cpp b/src/PJ_sconics.cpp new file mode 100644 index 00000000..4efec86f --- /dev/null +++ b/src/PJ_sconics.cpp @@ -0,0 +1,216 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + + +enum Type { + EULER = 0, + MURD1 = 1, + MURD2 = 2, + MURD3 = 3, + PCONIC = 4, + TISSOT = 5, + VITK1 = 6 +}; + +struct pj_opaque { + double n; + double rho_c; + double rho_0; + double sig; + double c1, c2; + enum Type type; +}; + + +#define EPS10 1.e-10 +#define EPS 1e-10 +#define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2=" + +PROJ_HEAD(euler, "Euler") LINE2; +PROJ_HEAD(murd1, "Murdoch I") LINE2; +PROJ_HEAD(murd2, "Murdoch II") LINE2; +PROJ_HEAD(murd3, "Murdoch III") LINE2; +PROJ_HEAD(pconic, "Perspective Conic") LINE2; +PROJ_HEAD(tissot, "Tissot") LINE2; +PROJ_HEAD(vitk1, "Vitkovsky I") LINE2; + + + +/* get common factors for simple conics */ +static int phi12(PJ *P, double *del) { + double p1, p2; + int err = 0; + + if (!pj_param(P->ctx, P->params, "tlat_1").i || + !pj_param(P->ctx, P->params, "tlat_2").i) { + err = -41; + } else { + p1 = pj_param(P->ctx, P->params, "rlat_1").f; + p2 = pj_param(P->ctx, P->params, "rlat_2").f; + *del = 0.5 * (p2 - p1); + static_cast(P->opaque)->sig = 0.5 * (p2 + p1); + err = (fabs(*del) < EPS || fabs(static_cast(P->opaque)->sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0; + } + return err; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rho; + + switch (Q->type) { + case MURD2: + rho = Q->rho_c + tan (Q->sig - lp.phi); + break; + case PCONIC: + rho = Q->c2 * (Q->c1 - tan (lp.phi - Q->sig)); + break; + default: + rho = Q->rho_c - lp.phi; + break; + } + + xy.x = rho * sin ( lp.lam *= Q->n ); + xy.y = Q->rho_0 - rho * cos (lp.lam); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */ + LP lp = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rho; + + rho = hypot (xy.x, xy.y = Q->rho_0 - xy.y); + if (Q->n < 0.) { + rho = - rho; + xy.x = - xy.x; + xy.y = - xy.y; + } + + lp.lam = atan2 (xy.x, xy.y) / Q->n; + + switch (Q->type) { + case PCONIC: + lp.phi = atan (Q->c1 - rho / Q->c2) + Q->sig; + break; + case MURD2: + lp.phi = Q->sig - atan(rho - Q->rho_c); + break; + default: + lp.phi = Q->rho_c - rho; + } + return lp; +} + + +static PJ *setup(PJ *P, enum Type type) { + double del, cs; + int err; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + Q->type = type; + + err = phi12 (P, &del); + if(err) + return pj_default_destructor (P, err); + + switch (Q->type) { + + case TISSOT: + Q->n = sin (Q->sig); + cs = cos (del); + Q->rho_c = Q->n / cs + cs / Q->n; + Q->rho_0 = sqrt ((Q->rho_c - 2 * sin (P->phi0)) / Q->n); + break; + + case MURD1: + Q->rho_c = sin(del)/(del * tan(Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + Q->n = sin(Q->sig); + break; + + case MURD2: + Q->rho_c = (cs = sqrt (cos (del))) / tan (Q->sig); + Q->rho_0 = Q->rho_c + tan (Q->sig - P->phi0); + Q->n = sin (Q->sig) * cs; + break; + + case MURD3: + Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + Q->n = sin (Q->sig) * sin (del) * tan (del) / (del * del); + break; + + case EULER: + Q->n = sin (Q->sig) * sin (del) / del; + del *= 0.5; + Q->rho_c = del / (tan (del) * tan (Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + break; + + case PCONIC: + Q->n = sin (Q->sig); + Q->c2 = cos (del); + Q->c1 = 1./tan (Q->sig); + if (fabs (del = P->phi0 - Q->sig) - EPS10 >= M_HALFPI) + return pj_default_destructor(P, PJD_ERR_LAT_0_HALF_PI_FROM_MEAN); + + Q->rho_0 = Q->c2 * (Q->c1 - tan (del)); + break; + + case VITK1: + Q->n = (cs = tan (del)) * sin (Q->sig) / del; + Q->rho_c = del / (cs * tan (Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + break; + } + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0; + return (P); +} + + +PJ *PROJECTION(euler) { + return setup(P, EULER); +} + + +PJ *PROJECTION(tissot) { + return setup(P, TISSOT); +} + + +PJ *PROJECTION(murd1) { + return setup(P, MURD1); +} + + +PJ *PROJECTION(murd2) { + return setup(P, MURD2); +} + + +PJ *PROJECTION(murd3) { + return setup(P, MURD3); +} + + +PJ *PROJECTION(pconic) { + return setup(P, PCONIC); +} + + +PJ *PROJECTION(vitk1) { + return setup(P, VITK1); +} + diff --git a/src/PJ_somerc.c b/src/PJ_somerc.c deleted file mode 100644 index c6c3ff21..00000000 --- a/src/PJ_somerc.c +++ /dev/null @@ -1,92 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(somerc, "Swiss. Obl. Mercator") "\n\tCyl, Ell\n\tFor CH1903"; - -struct pj_opaque { - double K, c, hlf_e, kR, cosp0, sinp0; -}; - -#define EPS 1.e-10 -#define NITER 6 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0, 0.0}; - double phip, lamp, phipp, lampp, sp, cp; - struct pj_opaque *Q = P->opaque; - - sp = P->e * sin (lp.phi); - phip = 2.* atan ( exp ( Q->c * ( - log (tan (M_FORTPI + 0.5 * lp.phi)) - Q->hlf_e * log ((1. + sp)/(1. - sp))) - + Q->K)) - M_HALFPI; - lamp = Q->c * lp.lam; - cp = cos(phip); - phipp = aasin (P->ctx, Q->cosp0 * sin (phip) - Q->sinp0 * cp * cos (lamp)); - lampp = aasin (P->ctx, cp * sin (lamp) / cos (phipp)); - xy.x = Q->kR * lampp; - xy.y = Q->kR * log (tan (M_FORTPI + 0.5 * phipp)); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double phip, lamp, phipp, lampp, cp, esp, con, delp; - int i; - - phipp = 2. * (atan (exp (xy.y / Q->kR)) - M_FORTPI); - lampp = xy.x / Q->kR; - cp = cos (phipp); - phip = aasin (P->ctx, Q->cosp0 * sin (phipp) + Q->sinp0 * cp * cos (lampp)); - lamp = aasin (P->ctx, cp * sin (lampp) / cos (phip)); - con = (Q->K - log (tan (M_FORTPI + 0.5 * phip)))/Q->c; - for (i = NITER; i ; --i) { - esp = P->e * sin(phip); - delp = (con + log(tan(M_FORTPI + 0.5 * phip)) - Q->hlf_e * - log((1. + esp)/(1. - esp)) ) * - (1. - esp * esp) * cos(phip) * P->rone_es; - phip -= delp; - if (fabs(delp) < EPS) - break; - } - if (i) { - lp.phi = phip; - lp.lam = lamp / Q->c; - } else { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - return (lp); -} - - -PJ *PROJECTION(somerc) { - double cp, phip0, sp; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - - Q->hlf_e = 0.5 * P->e; - cp = cos (P->phi0); - cp *= cp; - Q->c = sqrt (1 + P->es * cp * cp * P->rone_es); - sp = sin (P->phi0); - Q->cosp0 = cos( phip0 = aasin (P->ctx, Q->sinp0 = sp / Q->c) ); - sp *= P->e; - Q->K = log (tan (M_FORTPI + 0.5 * phip0)) - Q->c * ( - log (tan (M_FORTPI + 0.5 * P->phi0)) - Q->hlf_e * - log ((1. + sp) / (1. - sp))); - Q->kR = P->k0 * sqrt(P->one_es) / (1. - sp * sp); - P->inv = e_inverse; - P->fwd = e_forward; - return P; -} diff --git a/src/PJ_somerc.cpp b/src/PJ_somerc.cpp new file mode 100644 index 00000000..6a98f76f --- /dev/null +++ b/src/PJ_somerc.cpp @@ -0,0 +1,92 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(somerc, "Swiss. Obl. Mercator") "\n\tCyl, Ell\n\tFor CH1903"; + +struct pj_opaque { + double K, c, hlf_e, kR, cosp0, sinp0; +}; + +#define EPS 1.e-10 +#define NITER 6 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0, 0.0}; + double phip, lamp, phipp, lampp, sp, cp; + struct pj_opaque *Q = static_cast(P->opaque); + + sp = P->e * sin (lp.phi); + phip = 2.* atan ( exp ( Q->c * ( + log (tan (M_FORTPI + 0.5 * lp.phi)) - Q->hlf_e * log ((1. + sp)/(1. - sp))) + + Q->K)) - M_HALFPI; + lamp = Q->c * lp.lam; + cp = cos(phip); + phipp = aasin (P->ctx, Q->cosp0 * sin (phip) - Q->sinp0 * cp * cos (lamp)); + lampp = aasin (P->ctx, cp * sin (lamp) / cos (phipp)); + xy.x = Q->kR * lampp; + xy.y = Q->kR * log (tan (M_FORTPI + 0.5 * phipp)); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double phip, lamp, phipp, lampp, cp, esp, con, delp; + int i; + + phipp = 2. * (atan (exp (xy.y / Q->kR)) - M_FORTPI); + lampp = xy.x / Q->kR; + cp = cos (phipp); + phip = aasin (P->ctx, Q->cosp0 * sin (phipp) + Q->sinp0 * cp * cos (lampp)); + lamp = aasin (P->ctx, cp * sin (lampp) / cos (phip)); + con = (Q->K - log (tan (M_FORTPI + 0.5 * phip)))/Q->c; + for (i = NITER; i ; --i) { + esp = P->e * sin(phip); + delp = (con + log(tan(M_FORTPI + 0.5 * phip)) - Q->hlf_e * + log((1. + esp)/(1. - esp)) ) * + (1. - esp * esp) * cos(phip) * P->rone_es; + phip -= delp; + if (fabs(delp) < EPS) + break; + } + if (i) { + lp.phi = phip; + lp.lam = lamp / Q->c; + } else { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + return (lp); +} + + +PJ *PROJECTION(somerc) { + double cp, phip0, sp; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + + Q->hlf_e = 0.5 * P->e; + cp = cos (P->phi0); + cp *= cp; + Q->c = sqrt (1 + P->es * cp * cp * P->rone_es); + sp = sin (P->phi0); + Q->cosp0 = cos( phip0 = aasin (P->ctx, Q->sinp0 = sp / Q->c) ); + sp *= P->e; + Q->K = log (tan (M_FORTPI + 0.5 * phip0)) - Q->c * ( + log (tan (M_FORTPI + 0.5 * P->phi0)) - Q->hlf_e * + log ((1. + sp) / (1. - sp))); + Q->kR = P->k0 * sqrt(P->one_es) / (1. - sp * sp); + P->inv = e_inverse; + P->fwd = e_forward; + return P; +} diff --git a/src/PJ_stere.c b/src/PJ_stere.c deleted file mode 100644 index 82fd5c07..00000000 --- a/src/PJ_stere.c +++ /dev/null @@ -1,316 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - -PROJ_HEAD(stere, "Stereographic") "\n\tAzi, Sph&Ell\n\tlat_ts="; -PROJ_HEAD(ups, "Universal Polar Stereographic") "\n\tAzi, Sph&Ell\n\tsouth"; - - -enum Mode { - S_POLE = 0, - N_POLE = 1, - OBLIQ = 2, - EQUIT = 3 -}; - -struct pj_opaque { - double phits; - double sinX1; - double cosX1; - double akm1; - enum Mode mode; -}; - -#define sinph0 P->opaque->sinX1 -#define cosph0 P->opaque->cosX1 -#define EPS10 1.e-10 -#define TOL 1.e-8 -#define NITER 8 -#define CONV 1.e-10 - -static double ssfn_ (double phit, double sinphi, double eccen) { - sinphi *= eccen; - return (tan (.5 * (M_HALFPI + phit)) * - pow ((1. - sinphi) / (1. + sinphi), .5 * eccen)); -} - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double coslam, sinlam, sinX = 0.0, cosX = 0.0, X, A = 0.0, sinphi; - - coslam = cos (lp.lam); - sinlam = sin (lp.lam); - sinphi = sin (lp.phi); - if (Q->mode == OBLIQ || Q->mode == EQUIT) { - sinX = sin (X = 2. * atan(ssfn_(lp.phi, sinphi, P->e)) - M_HALFPI); - cosX = cos (X); - } - - switch (Q->mode) { - case OBLIQ: - A = Q->akm1 / (Q->cosX1 * (1. + Q->sinX1 * sinX + - Q->cosX1 * cosX * coslam)); - xy.y = A * (Q->cosX1 * sinX - Q->sinX1 * cosX * coslam); - goto xmul; /* but why not just xy.x = A * cosX; break; ? */ - - case EQUIT: - /* avoid zero division */ - if (1. + cosX * coslam == 0.0) { - xy.y = HUGE_VAL; - } else { - A = Q->akm1 / (1. + cosX * coslam); - xy.y = A * sinX; - } -xmul: - xy.x = A * cosX; - break; - - case S_POLE: - lp.phi = -lp.phi; - coslam = - coslam; - sinphi = -sinphi; - /*-fallthrough*/ - case N_POLE: - xy.x = Q->akm1 * pj_tsfn (lp.phi, sinphi, P->e); - xy.y = - xy.x * coslam; - break; - } - - xy.x = xy.x * sinlam; - return xy; -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sinphi, cosphi, coslam, sinlam; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - sinlam = sin(lp.lam); - - switch (Q->mode) { - case EQUIT: - xy.y = 1. + cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam; -oblcon: - if (xy.y <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = (xy.y = Q->akm1 / xy.y) * cosphi * sinlam; - xy.y *= (Q->mode == EQUIT) ? sinphi : - cosph0 * sinphi - sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = - coslam; - lp.phi = - lp.phi; - /*-fallthrough*/ - case S_POLE: - if (fabs (lp.phi - M_HALFPI) < TOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = sinlam * ( xy.y = Q->akm1 * tan (M_FORTPI + .5 * lp.phi) ); - xy.y *= coslam; - break; - } - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosphi, sinphi, tp=0.0, phi_l=0.0, rho, halfe=0.0, halfpi=0.0; - int i; - - rho = hypot (xy.x, xy.y); - - switch (Q->mode) { - case OBLIQ: - case EQUIT: - cosphi = cos ( tp = 2. * atan2 (rho * Q->cosX1 , Q->akm1) ); - sinphi = sin (tp); - if ( rho == 0.0 ) - phi_l = asin (cosphi * Q->sinX1); - else - phi_l = asin (cosphi * Q->sinX1 + (xy.y * sinphi * Q->cosX1 / rho)); - - tp = tan (.5 * (M_HALFPI + phi_l)); - xy.x *= sinphi; - xy.y = rho * Q->cosX1 * cosphi - xy.y * Q->sinX1* sinphi; - halfpi = M_HALFPI; - halfe = .5 * P->e; - break; - case N_POLE: - xy.y = -xy.y; - /*-fallthrough*/ - case S_POLE: - phi_l = M_HALFPI - 2. * atan (tp = - rho / Q->akm1); - halfpi = -M_HALFPI; - halfe = -.5 * P->e; - break; - } - - for (i = NITER; i--; phi_l = lp.phi) { - sinphi = P->e * sin(phi_l); - lp.phi = 2. * atan (tp * pow ((1.+sinphi)/(1.-sinphi), halfe)) - halfpi; - if (fabs (phi_l - lp.phi) < CONV) { - if (Q->mode == S_POLE) - lp.phi = -lp.phi; - lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); - return lp; - } - } - - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c, rh, sinc, cosc; - - sinc = sin (c = 2. * atan ((rh = hypot (xy.x, xy.y)) / Q->akm1)); - cosc = cos (c); - lp.lam = 0.; - - switch (Q->mode) { - case EQUIT: - if (fabs (rh) <= EPS10) - lp.phi = 0.; - else - lp.phi = asin (xy.y * sinc / rh); - if (cosc != 0. || xy.x != 0.) - lp.lam = atan2 (xy.x * sinc, cosc * rh); - break; - case OBLIQ: - if (fabs (rh) <= EPS10) - lp.phi = P->phi0; - else - lp.phi = asin (cosc * sinph0 + xy.y * sinc * cosph0 / rh); - if ((c = cosc - sinph0 * sin (lp.phi)) != 0. || xy.x != 0.) - lp.lam = atan2 (xy.x * sinc * cosph0, c * rh); - break; - case N_POLE: - xy.y = -xy.y; - /*-fallthrough*/ - case S_POLE: - if (fabs (rh) <= EPS10) - lp.phi = P->phi0; - else - lp.phi = asin (Q->mode == S_POLE ? - cosc : cosc); - lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); - break; - } - return lp; -} - - -static PJ *setup(PJ *P) { /* general initialization */ - double t; - struct pj_opaque *Q = P->opaque; - - if (fabs ((t = fabs (P->phi0)) - M_HALFPI) < EPS10) - Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else - Q->mode = t > EPS10 ? OBLIQ : EQUIT; - Q->phits = fabs (Q->phits); - - if (P->es != 0.0) { - double X; - - switch (Q->mode) { - case N_POLE: - case S_POLE: - if (fabs (Q->phits - M_HALFPI) < EPS10) - Q->akm1 = 2. * P->k0 / - sqrt (pow (1+P->e,1+P->e) * pow (1-P->e,1-P->e)); - else { - Q->akm1 = cos (Q->phits) / - pj_tsfn (Q->phits, t = sin (Q->phits), P->e); - t *= P->e; - Q->akm1 /= sqrt(1. - t * t); - } - break; - case EQUIT: - case OBLIQ: - t = sin (P->phi0); - X = 2. * atan (ssfn_(P->phi0, t, P->e)) - M_HALFPI; - t *= P->e; - Q->akm1 = 2. * P->k0 * cos (P->phi0) / sqrt(1. - t * t); - Q->sinX1 = sin (X); - Q->cosX1 = cos (X); - break; - } - P->inv = e_inverse; - P->fwd = e_forward; - } else { - switch (Q->mode) { - case OBLIQ: - sinph0 = sin (P->phi0); - cosph0 = cos (P->phi0); - /*-fallthrough*/ - case EQUIT: - Q->akm1 = 2. * P->k0; - break; - case S_POLE: - case N_POLE: - Q->akm1 = fabs (Q->phits - M_HALFPI) >= EPS10 ? - cos (Q->phits) / tan (M_FORTPI - .5 * Q->phits) : - 2. * P->k0 ; - break; - } - - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; -} - - -PJ *PROJECTION(stere) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->phits = pj_param (P->ctx, P->params, "tlat_ts").i ? - pj_param (P->ctx, P->params, "rlat_ts").f : M_HALFPI; - - return setup(P); -} - - -PJ *PROJECTION(ups) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - /* International Ellipsoid */ - P->phi0 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI; - if (P->es == 0.0) { - proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); - return pj_default_destructor (P, ENOMEM); - } - P->k0 = .994; - P->x0 = 2000000.; - P->y0 = 2000000.; - Q->phits = M_HALFPI; - P->lam0 = 0.; - - return setup(P); -} - diff --git a/src/PJ_stere.cpp b/src/PJ_stere.cpp new file mode 100644 index 00000000..94e7f91d --- /dev/null +++ b/src/PJ_stere.cpp @@ -0,0 +1,316 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + +PROJ_HEAD(stere, "Stereographic") "\n\tAzi, Sph&Ell\n\tlat_ts="; +PROJ_HEAD(ups, "Universal Polar Stereographic") "\n\tAzi, Sph&Ell\n\tsouth"; + + +enum Mode { + S_POLE = 0, + N_POLE = 1, + OBLIQ = 2, + EQUIT = 3 +}; + +struct pj_opaque { + double phits; + double sinX1; + double cosX1; + double akm1; + enum Mode mode; +}; + +#define sinph0 static_cast(P->opaque)->sinX1 +#define cosph0 static_cast(P->opaque)->cosX1 +#define EPS10 1.e-10 +#define TOL 1.e-8 +#define NITER 8 +#define CONV 1.e-10 + +static double ssfn_ (double phit, double sinphi, double eccen) { + sinphi *= eccen; + return (tan (.5 * (M_HALFPI + phit)) * + pow ((1. - sinphi) / (1. + sinphi), .5 * eccen)); +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double coslam, sinlam, sinX = 0.0, cosX = 0.0, X, A = 0.0, sinphi; + + coslam = cos (lp.lam); + sinlam = sin (lp.lam); + sinphi = sin (lp.phi); + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinX = sin (X = 2. * atan(ssfn_(lp.phi, sinphi, P->e)) - M_HALFPI); + cosX = cos (X); + } + + switch (Q->mode) { + case OBLIQ: + A = Q->akm1 / (Q->cosX1 * (1. + Q->sinX1 * sinX + + Q->cosX1 * cosX * coslam)); + xy.y = A * (Q->cosX1 * sinX - Q->sinX1 * cosX * coslam); + goto xmul; /* but why not just xy.x = A * cosX; break; ? */ + + case EQUIT: + /* avoid zero division */ + if (1. + cosX * coslam == 0.0) { + xy.y = HUGE_VAL; + } else { + A = Q->akm1 / (1. + cosX * coslam); + xy.y = A * sinX; + } +xmul: + xy.x = A * cosX; + break; + + case S_POLE: + lp.phi = -lp.phi; + coslam = - coslam; + sinphi = -sinphi; + /*-fallthrough*/ + case N_POLE: + xy.x = Q->akm1 * pj_tsfn (lp.phi, sinphi, P->e); + xy.y = - xy.x * coslam; + break; + } + + xy.x = xy.x * sinlam; + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sinphi, cosphi, coslam, sinlam; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + sinlam = sin(lp.lam); + + switch (Q->mode) { + case EQUIT: + xy.y = 1. + cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam; +oblcon: + if (xy.y <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = (xy.y = Q->akm1 / xy.y) * cosphi * sinlam; + xy.y *= (Q->mode == EQUIT) ? sinphi : + cosph0 * sinphi - sinph0 * cosphi * coslam; + break; + case N_POLE: + coslam = - coslam; + lp.phi = - lp.phi; + /*-fallthrough*/ + case S_POLE: + if (fabs (lp.phi - M_HALFPI) < TOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = sinlam * ( xy.y = Q->akm1 * tan (M_FORTPI + .5 * lp.phi) ); + xy.y *= coslam; + break; + } + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosphi, sinphi, tp=0.0, phi_l=0.0, rho, halfe=0.0, halfpi=0.0; + int i; + + rho = hypot (xy.x, xy.y); + + switch (Q->mode) { + case OBLIQ: + case EQUIT: + cosphi = cos ( tp = 2. * atan2 (rho * Q->cosX1 , Q->akm1) ); + sinphi = sin (tp); + if ( rho == 0.0 ) + phi_l = asin (cosphi * Q->sinX1); + else + phi_l = asin (cosphi * Q->sinX1 + (xy.y * sinphi * Q->cosX1 / rho)); + + tp = tan (.5 * (M_HALFPI + phi_l)); + xy.x *= sinphi; + xy.y = rho * Q->cosX1 * cosphi - xy.y * Q->sinX1* sinphi; + halfpi = M_HALFPI; + halfe = .5 * P->e; + break; + case N_POLE: + xy.y = -xy.y; + /*-fallthrough*/ + case S_POLE: + phi_l = M_HALFPI - 2. * atan (tp = - rho / Q->akm1); + halfpi = -M_HALFPI; + halfe = -.5 * P->e; + break; + } + + for (i = NITER; i--; phi_l = lp.phi) { + sinphi = P->e * sin(phi_l); + lp.phi = 2. * atan (tp * pow ((1.+sinphi)/(1.-sinphi), halfe)) - halfpi; + if (fabs (phi_l - lp.phi) < CONV) { + if (Q->mode == S_POLE) + lp.phi = -lp.phi; + lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); + return lp; + } + } + + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c, rh, sinc, cosc; + + sinc = sin (c = 2. * atan ((rh = hypot (xy.x, xy.y)) / Q->akm1)); + cosc = cos (c); + lp.lam = 0.; + + switch (Q->mode) { + case EQUIT: + if (fabs (rh) <= EPS10) + lp.phi = 0.; + else + lp.phi = asin (xy.y * sinc / rh); + if (cosc != 0. || xy.x != 0.) + lp.lam = atan2 (xy.x * sinc, cosc * rh); + break; + case OBLIQ: + if (fabs (rh) <= EPS10) + lp.phi = P->phi0; + else + lp.phi = asin (cosc * sinph0 + xy.y * sinc * cosph0 / rh); + if ((c = cosc - sinph0 * sin (lp.phi)) != 0. || xy.x != 0.) + lp.lam = atan2 (xy.x * sinc * cosph0, c * rh); + break; + case N_POLE: + xy.y = -xy.y; + /*-fallthrough*/ + case S_POLE: + if (fabs (rh) <= EPS10) + lp.phi = P->phi0; + else + lp.phi = asin (Q->mode == S_POLE ? - cosc : cosc); + lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); + break; + } + return lp; +} + + +static PJ *setup(PJ *P) { /* general initialization */ + double t; + struct pj_opaque *Q = static_cast(P->opaque); + + if (fabs ((t = fabs (P->phi0)) - M_HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else + Q->mode = t > EPS10 ? OBLIQ : EQUIT; + Q->phits = fabs (Q->phits); + + if (P->es != 0.0) { + double X; + + switch (Q->mode) { + case N_POLE: + case S_POLE: + if (fabs (Q->phits - M_HALFPI) < EPS10) + Q->akm1 = 2. * P->k0 / + sqrt (pow (1+P->e,1+P->e) * pow (1-P->e,1-P->e)); + else { + Q->akm1 = cos (Q->phits) / + pj_tsfn (Q->phits, t = sin (Q->phits), P->e); + t *= P->e; + Q->akm1 /= sqrt(1. - t * t); + } + break; + case EQUIT: + case OBLIQ: + t = sin (P->phi0); + X = 2. * atan (ssfn_(P->phi0, t, P->e)) - M_HALFPI; + t *= P->e; + Q->akm1 = 2. * P->k0 * cos (P->phi0) / sqrt(1. - t * t); + Q->sinX1 = sin (X); + Q->cosX1 = cos (X); + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } else { + switch (Q->mode) { + case OBLIQ: + sinph0 = sin (P->phi0); + cosph0 = cos (P->phi0); + /*-fallthrough*/ + case EQUIT: + Q->akm1 = 2. * P->k0; + break; + case S_POLE: + case N_POLE: + Q->akm1 = fabs (Q->phits - M_HALFPI) >= EPS10 ? + cos (Q->phits) / tan (M_FORTPI - .5 * Q->phits) : + 2. * P->k0 ; + break; + } + + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; +} + + +PJ *PROJECTION(stere) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->phits = pj_param (P->ctx, P->params, "tlat_ts").i ? + pj_param (P->ctx, P->params, "rlat_ts").f : M_HALFPI; + + return setup(P); +} + + +PJ *PROJECTION(ups) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + /* International Ellipsoid */ + P->phi0 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI; + if (P->es == 0.0) { + proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); + return pj_default_destructor (P, ENOMEM); + } + P->k0 = .994; + P->x0 = 2000000.; + P->y0 = 2000000.; + Q->phits = M_HALFPI; + P->lam0 = 0.; + + return setup(P); +} + diff --git a/src/PJ_sterea.c b/src/PJ_sterea.c deleted file mode 100644 index eb4c9f2c..00000000 --- a/src/PJ_sterea.c +++ /dev/null @@ -1,115 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2003 Gerald I. Evenden -*/ -/* -** 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. -*/ -#define PJ_LIB__ -#include -#include "projects.h" -#include "proj_math.h" - - -struct pj_opaque { - double phic0; - double cosc0, sinc0; - double R2; - void *en; -}; - - -PROJ_HEAD(sterea, "Oblique Stereographic Alternative") "\n\tAzimuthal, Sph&Ell"; - - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cosc, sinc, cosl, k; - - lp = pj_gauss(P->ctx, lp, Q->en); - sinc = sin(lp.phi); - cosc = cos(lp.phi); - cosl = cos(lp.lam); - k = P->k0 * Q->R2 / (1. + Q->sinc0 * sinc + Q->cosc0 * cosc * cosl); - xy.x = k * cosc * sin(lp.lam); - xy.y = k * (Q->cosc0 * sinc - Q->sinc0 * cosc * cosl); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double rho, c, sinc, cosc; - - xy.x /= P->k0; - xy.y /= P->k0; - if ( (rho = hypot (xy.x, xy.y)) != 0.0 ) { - c = 2. * atan2 (rho, Q->R2); - sinc = sin (c); - cosc = cos (c); - lp.phi = asin (cosc * Q->sinc0 + xy.y * sinc * Q->cosc0 / rho); - lp.lam = atan2 (xy.x * sinc, rho * Q->cosc0 * cosc - xy.y * Q->sinc0 * sinc); - } else { - lp.phi = Q->phic0; - lp.lam = 0.; - } - return pj_inv_gauss(P->ctx, lp, Q->en); -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor (P, errlev); -} - - -PJ *PROJECTION(sterea) { - double R; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->en = pj_gauss_ini(P->e, P->phi0, &(Q->phic0), &R); - if (0==Q->en) - return pj_default_destructor (P, ENOMEM); - - Q->sinc0 = sin (Q->phic0); - Q->cosc0 = cos (Q->phic0); - Q->R2 = 2. * R; - - P->inv = e_inverse; - P->fwd = e_forward; - P->destructor = destructor; - - return P; -} - diff --git a/src/PJ_sterea.cpp b/src/PJ_sterea.cpp new file mode 100644 index 00000000..4c2fe2a3 --- /dev/null +++ b/src/PJ_sterea.cpp @@ -0,0 +1,115 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2003 Gerald I. Evenden +*/ +/* +** 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. +*/ +#define PJ_LIB__ +#include +#include "projects.h" +#include "proj_math.h" + + +struct pj_opaque { + double phic0; + double cosc0, sinc0; + double R2; + void *en; +}; + + +PROJ_HEAD(sterea, "Oblique Stereographic Alternative") "\n\tAzimuthal, Sph&Ell"; + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cosc, sinc, cosl, k; + + lp = pj_gauss(P->ctx, lp, Q->en); + sinc = sin(lp.phi); + cosc = cos(lp.phi); + cosl = cos(lp.lam); + k = P->k0 * Q->R2 / (1. + Q->sinc0 * sinc + Q->cosc0 * cosc * cosl); + xy.x = k * cosc * sin(lp.lam); + xy.y = k * (Q->cosc0 * sinc - Q->sinc0 * cosc * cosl); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double rho, c, sinc, cosc; + + xy.x /= P->k0; + xy.y /= P->k0; + if ( (rho = hypot (xy.x, xy.y)) != 0.0 ) { + c = 2. * atan2 (rho, Q->R2); + sinc = sin (c); + cosc = cos (c); + lp.phi = asin (cosc * Q->sinc0 + xy.y * sinc * Q->cosc0 / rho); + lp.lam = atan2 (xy.x * sinc, rho * Q->cosc0 * cosc - xy.y * Q->sinc0 * sinc); + } else { + lp.phi = Q->phic0; + lp.lam = 0.; + } + return pj_inv_gauss(P->ctx, lp, Q->en); +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor (P, errlev); +} + + +PJ *PROJECTION(sterea) { + double R; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->en = pj_gauss_ini(P->e, P->phi0, &(Q->phic0), &R); + if (0==Q->en) + return pj_default_destructor (P, ENOMEM); + + Q->sinc0 = sin (Q->phic0); + Q->cosc0 = cos (Q->phic0); + Q->R2 = 2. * R; + + P->inv = e_inverse; + P->fwd = e_forward; + P->destructor = destructor; + + return P; +} + diff --git a/src/PJ_sts.c b/src/PJ_sts.c deleted file mode 100644 index 66094178..00000000 --- a/src/PJ_sts.c +++ /dev/null @@ -1,107 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(kav5, "Kavraisky V") "\n\tPCyl, Sph"; -PROJ_HEAD(qua_aut, "Quartic Authalic") "\n\tPCyl, Sph"; -PROJ_HEAD(fouc, "Foucaut") "\n\tPCyl, Sph"; -PROJ_HEAD(mbt_s, "McBryde-Thomas Flat-Polar Sine (No. 1)") "\n\tPCyl, Sph"; - - -struct pj_opaque { - double C_x, C_y, C_p; - int tan_mode; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c; - - xy.x = Q->C_x * lp.lam * cos(lp.phi); - xy.y = Q->C_y; - lp.phi *= Q->C_p; - c = cos(lp.phi); - if (Q->tan_mode) { - xy.x *= c * c; - xy.y *= tan (lp.phi); - } else { - xy.x /= c; - xy.y *= sin (lp.phi); - } - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double c; - - xy.y /= Q->C_y; - c = cos (lp.phi = Q->tan_mode ? atan (xy.y) : aasin (P->ctx, xy.y)); - lp.phi /= Q->C_p; - lp.lam = xy.x / (Q->C_x * cos(lp.phi)); - if (Q->tan_mode) - lp.lam /= c * c; - else - lp.lam *= c; - return lp; -} - - -static PJ *setup(PJ *P, double p, double q, int mode) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - P->opaque->C_x = q / p; - P->opaque->C_y = p; - P->opaque->C_p = 1/ q; - P->opaque->tan_mode = mode; - return P; -} - - - -PJ *PROJECTION(fouc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - return setup(P, 2., 2., 1); -} - - - -PJ *PROJECTION(kav5) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - return setup(P, 1.50488, 1.35439, 0); -} - - - -PJ *PROJECTION(qua_aut) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - return setup(P, 2., 2., 0); -} - - - -PJ *PROJECTION(mbt_s) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - return setup(P, 1.48875, 1.36509, 0); -} diff --git a/src/PJ_sts.cpp b/src/PJ_sts.cpp new file mode 100644 index 00000000..4aece68e --- /dev/null +++ b/src/PJ_sts.cpp @@ -0,0 +1,107 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(kav5, "Kavraisky V") "\n\tPCyl, Sph"; +PROJ_HEAD(qua_aut, "Quartic Authalic") "\n\tPCyl, Sph"; +PROJ_HEAD(fouc, "Foucaut") "\n\tPCyl, Sph"; +PROJ_HEAD(mbt_s, "McBryde-Thomas Flat-Polar Sine (No. 1)") "\n\tPCyl, Sph"; + + +struct pj_opaque { + double C_x, C_y, C_p; + int tan_mode; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c; + + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.y = Q->C_y; + lp.phi *= Q->C_p; + c = cos(lp.phi); + if (Q->tan_mode) { + xy.x *= c * c; + xy.y *= tan (lp.phi); + } else { + xy.x /= c; + xy.y *= sin (lp.phi); + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double c; + + xy.y /= Q->C_y; + c = cos (lp.phi = Q->tan_mode ? atan (xy.y) : aasin (P->ctx, xy.y)); + lp.phi /= Q->C_p; + lp.lam = xy.x / (Q->C_x * cos(lp.phi)); + if (Q->tan_mode) + lp.lam /= c * c; + else + lp.lam *= c; + return lp; +} + + +static PJ *setup(PJ *P, double p, double q, int mode) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + static_cast(P->opaque)->C_x = q / p; + static_cast(P->opaque)->C_y = p; + static_cast(P->opaque)->C_p = 1/ q; + static_cast(P->opaque)->tan_mode = mode; + return P; +} + + + +PJ *PROJECTION(fouc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + return setup(P, 2., 2., 1); +} + + + +PJ *PROJECTION(kav5) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + return setup(P, 1.50488, 1.35439, 0); +} + + + +PJ *PROJECTION(qua_aut) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + return setup(P, 2., 2., 0); +} + + + +PJ *PROJECTION(mbt_s) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + return setup(P, 1.48875, 1.36509, 0); +} diff --git a/src/PJ_tcc.c b/src/PJ_tcc.c deleted file mode 100644 index 60ded63e..00000000 --- a/src/PJ_tcc.c +++ /dev/null @@ -1,34 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(tcc, "Transverse Central Cylindrical") "\n\tCyl, Sph, no inv"; - -#define EPS10 1.e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - double b, bt; - - b = cos (lp.phi) * sin (lp.lam); - if ((bt = 1. - b * b) < EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - xy.x = b / sqrt(bt); - xy.y = atan2 (tan (lp.phi) , cos (lp.lam)); - return xy; -} - - -PJ *PROJECTION(tcc) { - P->es = 0.; - P->fwd = s_forward; - P->inv = 0; - - return P; -} diff --git a/src/PJ_tcc.cpp b/src/PJ_tcc.cpp new file mode 100644 index 00000000..60ded63e --- /dev/null +++ b/src/PJ_tcc.cpp @@ -0,0 +1,34 @@ +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(tcc, "Transverse Central Cylindrical") "\n\tCyl, Sph, no inv"; + +#define EPS10 1.e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + double b, bt; + + b = cos (lp.phi) * sin (lp.lam); + if ((bt = 1. - b * b) < EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + xy.x = b / sqrt(bt); + xy.y = atan2 (tan (lp.phi) , cos (lp.lam)); + return xy; +} + + +PJ *PROJECTION(tcc) { + P->es = 0.; + P->fwd = s_forward; + P->inv = 0; + + return P; +} diff --git a/src/PJ_tcea.c b/src/PJ_tcea.c deleted file mode 100644 index d30f3df0..00000000 --- a/src/PJ_tcea.c +++ /dev/null @@ -1,36 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(tcea, "Transverse Cylindrical Equal Area") "\n\tCyl, Sph"; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - xy.x = cos (lp.phi) * sin (lp.lam) / P->k0; - xy.y = P->k0 * (atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0); - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0, 0.0}; - double t; - - xy.y = xy.y / P->k0 + P->phi0; - xy.x *= P->k0; - t = sqrt (1. - xy.x * xy.x); - lp.phi = asin (t * sin (xy.y)); - lp.lam = atan2 (xy.x, t * cos (xy.y)); - return lp; -} - - -PJ *PROJECTION(tcea) { - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - return P; -} diff --git a/src/PJ_tcea.cpp b/src/PJ_tcea.cpp new file mode 100644 index 00000000..d30f3df0 --- /dev/null +++ b/src/PJ_tcea.cpp @@ -0,0 +1,36 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(tcea, "Transverse Cylindrical Equal Area") "\n\tCyl, Sph"; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = cos (lp.phi) * sin (lp.lam) / P->k0; + xy.y = P->k0 * (atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + double t; + + xy.y = xy.y / P->k0 + P->phi0; + xy.x *= P->k0; + t = sqrt (1. - xy.x * xy.x); + lp.phi = asin (t * sin (xy.y)); + lp.lam = atan2 (xy.x, t * cos (xy.y)); + return lp; +} + + +PJ *PROJECTION(tcea) { + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} diff --git a/src/PJ_times.c b/src/PJ_times.c deleted file mode 100644 index e8b4499f..00000000 --- a/src/PJ_times.c +++ /dev/null @@ -1,79 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the Times projection. - * Author: Kristian Evers - * - ****************************************************************************** - * Copyright (c) 2016, Kristian Evers - * - * 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. - ***************************************************************************** - * Based on describtion of the Times Projection in - * - * Flattening the Earth, Snyder, J.P., 1993, p.213-214. - *****************************************************************************/ - -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(times, "Times") "\n\tCyl, Sph"; - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - double T, S, S2; - XY xy = {0.0,0.0}; - (void) P; - - T = tan(lp.phi/2.0); - S = sin(M_FORTPI * T); - S2 = S*S; - - xy.x = lp.lam * (0.74482 - 0.34588*S2); - xy.y = 1.70711 * T; - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - double T, S, S2; - LP lp = {0.0,0.0}; - (void) P; - - T = xy.y / 1.70711; - S = sin(M_FORTPI * T); - S2 = S*S; - - lp.lam = xy.x / (0.74482 - 0.34588 * S2); - lp.phi = 2 * atan(T); - - return lp; -} - - -PJ *PROJECTION(times) { - P->es = 0.0; - - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_times.cpp b/src/PJ_times.cpp new file mode 100644 index 00000000..e8b4499f --- /dev/null +++ b/src/PJ_times.cpp @@ -0,0 +1,79 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the Times projection. + * Author: Kristian Evers + * + ****************************************************************************** + * Copyright (c) 2016, Kristian Evers + * + * 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. + ***************************************************************************** + * Based on describtion of the Times Projection in + * + * Flattening the Earth, Snyder, J.P., 1993, p.213-214. + *****************************************************************************/ + +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(times, "Times") "\n\tCyl, Sph"; + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + double T, S, S2; + XY xy = {0.0,0.0}; + (void) P; + + T = tan(lp.phi/2.0); + S = sin(M_FORTPI * T); + S2 = S*S; + + xy.x = lp.lam * (0.74482 - 0.34588*S2); + xy.y = 1.70711 * T; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + double T, S, S2; + LP lp = {0.0,0.0}; + (void) P; + + T = xy.y / 1.70711; + S = sin(M_FORTPI * T); + S2 = S*S; + + lp.lam = xy.x / (0.74482 - 0.34588 * S2); + lp.phi = 2 * atan(T); + + return lp; +} + + +PJ *PROJECTION(times) { + P->es = 0.0; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_tmerc.c b/src/PJ_tmerc.c deleted file mode 100644 index 069cdc2c..00000000 --- a/src/PJ_tmerc.c +++ /dev/null @@ -1,208 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(tmerc, "Transverse Mercator") "\n\tCyl, Sph&Ell"; - - -struct pj_opaque { - double esp; - double ml0; - double *en; -}; - -#define EPS10 1.e-10 -#define FC1 1. -#define FC2 .5 -#define FC3 .16666666666666666666 -#define FC4 .08333333333333333333 -#define FC5 .05 -#define FC6 .03333333333333333333 -#define FC7 .02380952380952380952 -#define FC8 .01785714285714285714 - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double al, als, n, cosphi, sinphi, t; - - /* - * Fail if our longitude is more than 90 degrees from the - * central meridian since the results are essentially garbage. - * Is error -20 really an appropriate return value? - * - * http://trac.osgeo.org/proj/ticket/5 - */ - if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) { - xy.x = HUGE_VAL; - xy.y = HUGE_VAL; - pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT ); - return xy; - } - - sinphi = sin (lp.phi); - cosphi = cos (lp.phi); - t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; - t *= t; - al = cosphi * lp.lam; - als = al * al; - al /= sqrt (1. - P->es * sinphi * sinphi); - n = Q->esp * cosphi * cosphi; - xy.x = P->k0 * al * (FC1 + - FC3 * als * (1. - t + n + - FC5 * als * (5. + t * (t - 18.) + n * (14. - 58. * t) - + FC7 * als * (61. + t * ( t * (179. - t) - 479. ) ) - ))); - xy.y = P->k0 * (pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->ml0 + - sinphi * al * lp.lam * FC2 * ( 1. + - FC4 * als * (5. - t + n * (9. + 4. * n) + - FC6 * als * (61. + t * (t - 58.) + n * (270. - 330 * t) - + FC8 * als * (1385. + t * ( t * (543. - t) - 3111.) ) - )))); - return (xy); -} - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double b, cosphi; - - /* - * Fail if our longitude is more than 90 degrees from the - * central meridian since the results are essentially garbage. - * Is error -20 really an appropriate return value? - * - * http://trac.osgeo.org/proj/ticket/5 - */ - if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) { - xy.x = HUGE_VAL; - xy.y = HUGE_VAL; - pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT ); - return xy; - } - - cosphi = cos(lp.phi); - b = cosphi * sin (lp.lam); - if (fabs (fabs (b) - 1.) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - xy.x = P->opaque->ml0 * log ((1. + b) / (1. - b)); - xy.y = cosphi * cos (lp.lam) / sqrt (1. - b * b); - - b = fabs ( xy.y ); - if (b >= 1.) { - if ((b - 1.) > EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - else xy.y = 0.; - } else - xy.y = acos (xy.y); - - if (lp.phi < 0.) - xy.y = -xy.y; - xy.y = P->opaque->esp * (xy.y - P->phi0); - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double n, con, cosphi, d, ds, sinphi, t; - - lp.phi = pj_inv_mlfn(P->ctx, Q->ml0 + xy.y / P->k0, P->es, Q->en); - if (fabs(lp.phi) >= M_HALFPI) { - lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; - lp.lam = 0.; - } else { - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; - n = Q->esp * cosphi * cosphi; - d = xy.x * sqrt (con = 1. - P->es * sinphi * sinphi) / P->k0; - con *= t; - t *= t; - ds = d * d; - lp.phi -= (con * ds / (1.-P->es)) * FC2 * (1. - - ds * FC4 * (5. + t * (3. - 9. * n) + n * (1. - 4 * n) - - ds * FC6 * (61. + t * (90. - 252. * n + - 45. * t) + 46. * n - - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1575. * t)) ) - ))); - lp.lam = d*(FC1 - - ds*FC3*( 1. + 2.*t + n - - ds*FC5*(5. + t*(28. + 24.*t + 8.*n) + 6.*n - - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t)) ) - ))) / cosphi; - } - return lp; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0, 0.0}; - double h, g; - - h = exp(xy.x / P->opaque->esp); - g = .5 * (h - 1. / h); - h = cos (P->phi0 + xy.y / P->opaque->esp); - lp.phi = asin(sqrt((1. - h * h) / (1. + g * g))); - - /* Make sure that phi is on the correct hemisphere when false northing is used */ - if (xy.y < 0. && -lp.phi+P->phi0 < 0.0) lp.phi = -lp.phi; - - lp.lam = (g != 0.0 || h != 0.0) ? atan2 (g, h) : 0.; - return lp; -} - - -static void *destructor(PJ *P, int errlev) { /* Destructor */ - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor(P, errlev); - - pj_dealloc (P->opaque->en); - return pj_default_destructor(P, errlev); -} - - -static PJ *setup(PJ *P) { /* general initialization */ - struct pj_opaque *Q = P->opaque; - if (P->es != 0.0) { - if (!(Q->en = pj_enfn(P->es))) - return pj_default_destructor(P, ENOMEM); - - Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); - Q->esp = P->es / (1. - P->es); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - Q->esp = P->k0; - Q->ml0 = .5 * Q->esp; - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; -} - - -PJ *PROJECTION(tmerc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - - P->opaque = Q; - P->destructor = destructor; - - return setup(P); -} diff --git a/src/PJ_tmerc.cpp b/src/PJ_tmerc.cpp new file mode 100644 index 00000000..55f878c9 --- /dev/null +++ b/src/PJ_tmerc.cpp @@ -0,0 +1,208 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(tmerc, "Transverse Mercator") "\n\tCyl, Sph&Ell"; + + +struct pj_opaque { + double esp; + double ml0; + double *en; +}; + +#define EPS10 1.e-10 +#define FC1 1. +#define FC2 .5 +#define FC3 .16666666666666666666 +#define FC4 .08333333333333333333 +#define FC5 .05 +#define FC6 .03333333333333333333 +#define FC7 .02380952380952380952 +#define FC8 .01785714285714285714 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double al, als, n, cosphi, sinphi, t; + + /* + * Fail if our longitude is more than 90 degrees from the + * central meridian since the results are essentially garbage. + * Is error -20 really an appropriate return value? + * + * http://trac.osgeo.org/proj/ticket/5 + */ + if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) { + xy.x = HUGE_VAL; + xy.y = HUGE_VAL; + pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT ); + return xy; + } + + sinphi = sin (lp.phi); + cosphi = cos (lp.phi); + t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; + t *= t; + al = cosphi * lp.lam; + als = al * al; + al /= sqrt (1. - P->es * sinphi * sinphi); + n = Q->esp * cosphi * cosphi; + xy.x = P->k0 * al * (FC1 + + FC3 * als * (1. - t + n + + FC5 * als * (5. + t * (t - 18.) + n * (14. - 58. * t) + + FC7 * als * (61. + t * ( t * (179. - t) - 479. ) ) + ))); + xy.y = P->k0 * (pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->ml0 + + sinphi * al * lp.lam * FC2 * ( 1. + + FC4 * als * (5. - t + n * (9. + 4. * n) + + FC6 * als * (61. + t * (t - 58.) + n * (270. - 330 * t) + + FC8 * als * (1385. + t * ( t * (543. - t) - 3111.) ) + )))); + return (xy); +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double b, cosphi; + + /* + * Fail if our longitude is more than 90 degrees from the + * central meridian since the results are essentially garbage. + * Is error -20 really an appropriate return value? + * + * http://trac.osgeo.org/proj/ticket/5 + */ + if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) { + xy.x = HUGE_VAL; + xy.y = HUGE_VAL; + pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT ); + return xy; + } + + cosphi = cos(lp.phi); + b = cosphi * sin (lp.lam); + if (fabs (fabs (b) - 1.) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + xy.x = static_cast(P->opaque)->ml0 * log ((1. + b) / (1. - b)); + xy.y = cosphi * cos (lp.lam) / sqrt (1. - b * b); + + b = fabs ( xy.y ); + if (b >= 1.) { + if ((b - 1.) > EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + else xy.y = 0.; + } else + xy.y = acos (xy.y); + + if (lp.phi < 0.) + xy.y = -xy.y; + xy.y = static_cast(P->opaque)->esp * (xy.y - P->phi0); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double n, con, cosphi, d, ds, sinphi, t; + + lp.phi = pj_inv_mlfn(P->ctx, Q->ml0 + xy.y / P->k0, P->es, Q->en); + if (fabs(lp.phi) >= M_HALFPI) { + lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI; + lp.lam = 0.; + } else { + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; + n = Q->esp * cosphi * cosphi; + d = xy.x * sqrt (con = 1. - P->es * sinphi * sinphi) / P->k0; + con *= t; + t *= t; + ds = d * d; + lp.phi -= (con * ds / (1.-P->es)) * FC2 * (1. - + ds * FC4 * (5. + t * (3. - 9. * n) + n * (1. - 4 * n) - + ds * FC6 * (61. + t * (90. - 252. * n + + 45. * t) + 46. * n + - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1575. * t)) ) + ))); + lp.lam = d*(FC1 - + ds*FC3*( 1. + 2.*t + n - + ds*FC5*(5. + t*(28. + 24.*t + 8.*n) + 6.*n + - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t)) ) + ))) / cosphi; + } + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + double h, g; + + h = exp(xy.x / static_cast(P->opaque)->esp); + g = .5 * (h - 1. / h); + h = cos (P->phi0 + xy.y / static_cast(P->opaque)->esp); + lp.phi = asin(sqrt((1. - h * h) / (1. + g * g))); + + /* Make sure that phi is on the correct hemisphere when false northing is used */ + if (xy.y < 0. && -lp.phi+P->phi0 < 0.0) lp.phi = -lp.phi; + + lp.lam = (g != 0.0 || h != 0.0) ? atan2 (g, h) : 0.; + return lp; +} + + +static PJ *destructor(PJ *P, int errlev) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor(P, errlev); + + pj_dealloc (static_cast(P->opaque)->en); + return pj_default_destructor(P, errlev); +} + + +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = static_cast(P->opaque); + if (P->es != 0.0) { + if (!(Q->en = pj_enfn(P->es))) + return pj_default_destructor(P, ENOMEM); + + Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); + Q->esp = P->es / (1. - P->es); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->esp = P->k0; + Q->ml0 = .5 * Q->esp; + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; +} + + +PJ *PROJECTION(tmerc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + + P->opaque = Q; + P->destructor = destructor; + + return setup(P); +} diff --git a/src/PJ_tobmerc.c b/src/PJ_tobmerc.c deleted file mode 100644 index 9c939f0b..00000000 --- a/src/PJ_tobmerc.c +++ /dev/null @@ -1,51 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj_internal.h" -#include "proj.h" -#include "proj_math.h" -#include "projects.h" - -PROJ_HEAD(tobmerc, "Tobler-Mercator") "\n\tCyl, Sph"; - -#define EPS10 1.e-10 -static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */ - if (fabs(x) <= DBL_EPSILON) { - /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */ - return log1p(x); - } - return log(tan(M_FORTPI + .5 * x)); -} - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - double cosphi; - - if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - - cosphi = cos(lp.phi); - xy.x = P->k0 * lp.lam * cosphi * cosphi; - xy.y = P->k0 * logtanpfpim1(lp.phi); - return xy; -} - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0, 0.0}; - double cosphi; - - lp.phi = atan(sinh(xy.y / P->k0)); - cosphi = cos(lp.phi); - lp.lam = xy.x / P->k0 / (cosphi * cosphi); - return lp; -} - -PJ *PROJECTION(tobmerc) { - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_tobmerc.cpp b/src/PJ_tobmerc.cpp new file mode 100644 index 00000000..9c939f0b --- /dev/null +++ b/src/PJ_tobmerc.cpp @@ -0,0 +1,51 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj_internal.h" +#include "proj.h" +#include "proj_math.h" +#include "projects.h" + +PROJ_HEAD(tobmerc, "Tobler-Mercator") "\n\tCyl, Sph"; + +#define EPS10 1.e-10 +static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */ + if (fabs(x) <= DBL_EPSILON) { + /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */ + return log1p(x); + } + return log(tan(M_FORTPI + .5 * x)); +} + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + double cosphi; + + if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + + cosphi = cos(lp.phi); + xy.x = P->k0 * lp.lam * cosphi * cosphi; + xy.y = P->k0 * logtanpfpim1(lp.phi); + return xy; +} + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + double cosphi; + + lp.phi = atan(sinh(xy.y / P->k0)); + cosphi = cos(lp.phi); + lp.lam = xy.x / P->k0 / (cosphi * cosphi); + return lp; +} + +PJ *PROJECTION(tobmerc) { + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_tpeqd.c b/src/PJ_tpeqd.c deleted file mode 100644 index 87877ec1..00000000 --- a/src/PJ_tpeqd.c +++ /dev/null @@ -1,107 +0,0 @@ -#define PJ_LIB__ -#include -#include "proj.h" -#include "proj_math.h" -#include "projects.h" - - -PROJ_HEAD(tpeqd, "Two Point Equidistant") - "\n\tMisc Sph\n\tlat_1= lon_1= lat_2= lon_2="; - -struct pj_opaque { - double cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2; - double hz0, thz0, rhshz0, ca, sa, lp, lamc; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double t, z1, z2, dl1, dl2, sp, cp; - - sp = sin(lp.phi); - cp = cos(lp.phi); - z1 = aacos(P->ctx, Q->sp1 * sp + Q->cp1 * cp * cos (dl1 = lp.lam + Q->dlam2)); - z2 = aacos(P->ctx, Q->sp2 * sp + Q->cp2 * cp * cos (dl2 = lp.lam - Q->dlam2)); - z1 *= z1; - z2 *= z2; - - xy.x = Q->r2z0 * (t = z1 - z2); - t = Q->z02 - t; - xy.y = Q->r2z0 * asqrt (4. * Q->z02 * z2 - t * t); - if ((Q->ccs * sp - cp * (Q->cs * sin(dl1) - Q->sc * sin(dl2))) < 0.) - xy.y = -xy.y; - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double cz1, cz2, s, d, cp, sp; - - cz1 = cos (hypot(xy.y, xy.x + Q->hz0)); - cz2 = cos (hypot(xy.y, xy.x - Q->hz0)); - s = cz1 + cz2; - d = cz1 - cz2; - lp.lam = - atan2(d, (s * Q->thz0)); - lp.phi = aacos(P->ctx, hypot (Q->thz0 * s, d) * Q->rhshz0); - if ( xy.y < 0. ) - lp.phi = - lp.phi; - /* lam--phi now in system relative to P1--P2 base equator */ - sp = sin (lp.phi); - cp = cos (lp.phi); - lp.phi = aasin (P->ctx, Q->sa * sp + Q->ca * cp * (s = cos(lp.lam -= Q->lp))); - lp.lam = atan2 (cp * sin(lp.lam), Q->sa * cp * s - Q->ca * sp) + Q->lamc; - return lp; -} - - -PJ *PROJECTION(tpeqd) { - double lam_1, lam_2, phi_1, phi_2, A12, pp; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - - /* get control point locations */ - phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; - lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; - phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; - lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; - - if (phi_1 == phi_2 && lam_1 == lam_2) - return pj_default_destructor(P, PJD_ERR_CONTROL_POINT_NO_DIST); - - P->lam0 = adjlon (0.5 * (lam_1 + lam_2)); - Q->dlam2 = adjlon (lam_2 - lam_1); - - Q->cp1 = cos (phi_1); - Q->cp2 = cos (phi_2); - Q->sp1 = sin (phi_1); - Q->sp2 = sin (phi_2); - Q->cs = Q->cp1 * Q->sp2; - Q->sc = Q->sp1 * Q->cp2; - Q->ccs = Q->cp1 * Q->cp2 * sin(Q->dlam2); - Q->z02 = aacos(P->ctx, Q->sp1 * Q->sp2 + Q->cp1 * Q->cp2 * cos (Q->dlam2)); - Q->hz0 = .5 * Q->z02; - A12 = atan2(Q->cp2 * sin (Q->dlam2), - Q->cp1 * Q->sp2 - Q->sp1 * Q->cp2 * cos (Q->dlam2)); - Q->ca = cos(pp = aasin(P->ctx, Q->cp1 * sin(A12))); - Q->sa = sin(pp); - Q->lp = adjlon ( atan2 (Q->cp1 * cos(A12), Q->sp1) - Q->hz0); - Q->dlam2 *= .5; - Q->lamc = M_HALFPI - atan2(sin(A12) * Q->sp1, cos(A12)) - Q->dlam2; - Q->thz0 = tan (Q->hz0); - Q->rhshz0 = .5 / sin (Q->hz0); - Q->r2z0 = 0.5 / Q->z02; - Q->z02 *= Q->z02; - - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - - return P; -} - diff --git a/src/PJ_tpeqd.cpp b/src/PJ_tpeqd.cpp new file mode 100644 index 00000000..5691cd7b --- /dev/null +++ b/src/PJ_tpeqd.cpp @@ -0,0 +1,107 @@ +#define PJ_LIB__ +#include +#include "proj.h" +#include "proj_math.h" +#include "projects.h" + + +PROJ_HEAD(tpeqd, "Two Point Equidistant") + "\n\tMisc Sph\n\tlat_1= lon_1= lat_2= lon_2="; + +struct pj_opaque { + double cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2; + double hz0, thz0, rhshz0, ca, sa, lp, lamc; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t, z1, z2, dl1, dl2, sp, cp; + + sp = sin(lp.phi); + cp = cos(lp.phi); + z1 = aacos(P->ctx, Q->sp1 * sp + Q->cp1 * cp * cos (dl1 = lp.lam + Q->dlam2)); + z2 = aacos(P->ctx, Q->sp2 * sp + Q->cp2 * cp * cos (dl2 = lp.lam - Q->dlam2)); + z1 *= z1; + z2 *= z2; + + xy.x = Q->r2z0 * (t = z1 - z2); + t = Q->z02 - t; + xy.y = Q->r2z0 * asqrt (4. * Q->z02 * z2 - t * t); + if ((Q->ccs * sp - cp * (Q->cs * sin(dl1) - Q->sc * sin(dl2))) < 0.) + xy.y = -xy.y; + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double cz1, cz2, s, d, cp, sp; + + cz1 = cos (hypot(xy.y, xy.x + Q->hz0)); + cz2 = cos (hypot(xy.y, xy.x - Q->hz0)); + s = cz1 + cz2; + d = cz1 - cz2; + lp.lam = - atan2(d, (s * Q->thz0)); + lp.phi = aacos(P->ctx, hypot (Q->thz0 * s, d) * Q->rhshz0); + if ( xy.y < 0. ) + lp.phi = - lp.phi; + /* lam--phi now in system relative to P1--P2 base equator */ + sp = sin (lp.phi); + cp = cos (lp.phi); + lp.phi = aasin (P->ctx, Q->sa * sp + Q->ca * cp * (s = cos(lp.lam -= Q->lp))); + lp.lam = atan2 (cp * sin(lp.lam), Q->sa * cp * s - Q->ca * sp) + Q->lamc; + return lp; +} + + +PJ *PROJECTION(tpeqd) { + double lam_1, lam_2, phi_1, phi_2, A12, pp; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + + /* get control point locations */ + phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; + lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; + phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; + lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; + + if (phi_1 == phi_2 && lam_1 == lam_2) + return pj_default_destructor(P, PJD_ERR_CONTROL_POINT_NO_DIST); + + P->lam0 = adjlon (0.5 * (lam_1 + lam_2)); + Q->dlam2 = adjlon (lam_2 - lam_1); + + Q->cp1 = cos (phi_1); + Q->cp2 = cos (phi_2); + Q->sp1 = sin (phi_1); + Q->sp2 = sin (phi_2); + Q->cs = Q->cp1 * Q->sp2; + Q->sc = Q->sp1 * Q->cp2; + Q->ccs = Q->cp1 * Q->cp2 * sin(Q->dlam2); + Q->z02 = aacos(P->ctx, Q->sp1 * Q->sp2 + Q->cp1 * Q->cp2 * cos (Q->dlam2)); + Q->hz0 = .5 * Q->z02; + A12 = atan2(Q->cp2 * sin (Q->dlam2), + Q->cp1 * Q->sp2 - Q->sp1 * Q->cp2 * cos (Q->dlam2)); + Q->ca = cos(pp = aasin(P->ctx, Q->cp1 * sin(A12))); + Q->sa = sin(pp); + Q->lp = adjlon ( atan2 (Q->cp1 * cos(A12), Q->sp1) - Q->hz0); + Q->dlam2 *= .5; + Q->lamc = M_HALFPI - atan2(sin(A12) * Q->sp1, cos(A12)) - Q->dlam2; + Q->thz0 = tan (Q->hz0); + Q->rhshz0 = .5 / sin (Q->hz0); + Q->r2z0 = 0.5 / Q->z02; + Q->z02 *= Q->z02; + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} + diff --git a/src/PJ_unitconvert.c b/src/PJ_unitconvert.c deleted file mode 100644 index 402941a4..00000000 --- a/src/PJ_unitconvert.c +++ /dev/null @@ -1,548 +0,0 @@ -/*********************************************************************** - - Unit conversion pseudo-projection for use with - transformation pipelines. - - Kristian Evers, 2017-05-16 - -************************************************************************ - -A pseudo-projection that can be used to convert units of input and -output data. Primarily useful in pipelines. - -Unit conversion is performed by means of a pivot unit. The pivot unit -for distance units are the meter and for time we use the modified julian -date. A time unit conversion is performed like - - Unit A -> Modified Julian date -> Unit B - -distance units are converted in the same manner, with meter being the -central unit. - -The modified Julian date is chosen as the pivot unit since it has a -fairly high precision, goes sufficiently long backwards in time, has no -danger of hitting the upper limit in the near future and it is a fairly -common time unit in astronomy and geodesy. Note that we are using the -Julian date and not day. The difference being that the latter is defined -as an integer and is thus limited to days in resolution. This approach -has been extended wherever it makes sense, e.g. the GPS week unit also -has a fractional part that makes it possible to determine the day, hour -and minute of an observation. - -In- and output units are controlled with the parameters - - +xy_in, +xy_out, +z_in, +z_out, +t_in and +t_out - -where xy denotes horizontal units, z vertical units and t time units. - -************************************************************************ - -Kristian Evers, kreve@sdfe.dk, 2017-05-09 -Last update: 2017-05-16 - -************************************************************************ -* Copyright (c) 2017, Kristian Evers / SDFE -* -* 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. -* -***********************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -PROJ_HEAD(unitconvert, "Unit conversion"); - -typedef double (*tconvert)(double); - -struct TIME_UNITS { - char *id; /* units keyword */ - tconvert t_in; /* unit -> mod. julian date function pointer */ - tconvert t_out; /* mod. julian date > unit function pointer */ - char *name; /* comments */ -}; - -struct pj_opaque_unitconvert { - int t_in_id; /* time unit id for the time input unit */ - int t_out_id; /* time unit id for the time output unit */ - double xy_factor; /* unit conversion factor for horizontal components */ - double z_factor; /* unit conversion factor for vertical components */ -}; - - -/***********************************************************************/ -static int is_leap_year(long year) { -/***********************************************************************/ - return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0); -} - - -/***********************************************************************/ -static int days_in_year(long year) { -/***********************************************************************/ - return is_leap_year(year) ? 366 : 365; -} - -/***********************************************************************/ -static unsigned int days_in_month(unsigned long year, unsigned long month) { -/***********************************************************************/ - const unsigned int month_table[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - unsigned int days; - - if (month > 12) month = 12; - if (month == 0) month = 1; - - days = month_table[month-1]; - if (is_leap_year(year) && month == 2) days++; - - return days; -} - - -/***********************************************************************/ -static int daynumber_in_year(unsigned long year, unsigned long month, unsigned long day) { -/***********************************************************************/ - unsigned int daynumber=0, i; - - if (month > 12) month = 12; - if (month == 0) month = 1; - if (day > days_in_month(year, month)) day = days_in_month(year, month); - - for (i = 1; i < month; i++) - daynumber += days_in_month(year, i); - - daynumber += day; - - return daynumber; - -} - -/***********************************************************************/ -static double mjd_to_mjd(double mjd) { -/*********************************************************************** - Modified julian date no-op function. - - The Julian date is defined as (fractional) days since midnight - on 16th of November in 1858. -************************************************************************/ - return mjd; -} - - -/***********************************************************************/ -static double decimalyear_to_mjd(double decimalyear) { -/*********************************************************************** - Epoch of modified julian date is 1858-11-16 00:00 -************************************************************************/ - long year; - double fractional_year; - double mjd; - - if( decimalyear < -10000 || decimalyear > 10000 ) - return 0; - - year = lround(floor(decimalyear)); - fractional_year = decimalyear - year; - mjd = (year - 1859)*365 + 14 + 31; - mjd += (double)fractional_year*(double)days_in_year(year); - - /* take care of leap days */ - year--; - for (; year > 1858; year--) - if (is_leap_year(year)) - mjd++; - - return mjd; -} - - -/***********************************************************************/ -static double mjd_to_decimalyear(double mjd) { -/*********************************************************************** - Epoch of modified julian date is 1858-11-16 00:00 -************************************************************************/ - double decimalyear = mjd; - double mjd_iter = 14 + 31; - int year = 1859; - - /* a smarter brain than mine could probably to do this more elegantly - - I'll just brute-force my way out of this... */ - for (; mjd >= mjd_iter; year++) { - mjd_iter += days_in_year(year); - } - year--; - mjd_iter -= days_in_year(year); - - decimalyear = year + (mjd-mjd_iter)/days_in_year(year); - return decimalyear; -} - - -/***********************************************************************/ -static double gps_week_to_mjd(double gps_week) { -/*********************************************************************** - GPS weeks are defined as the number of weeks since January the 6th - 1980. - - Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian - date is 44244. -************************************************************************/ - return 44244.0 + gps_week*7.0; -} - - -/***********************************************************************/ -static double mjd_to_gps_week(double mjd) { -/*********************************************************************** - GPS weeks are defined as the number of weeks since January the 6th - 1980. - - Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian - date is 44244. -************************************************************************/ - return (mjd - 44244.0) / 7.0; -} - - -/***********************************************************************/ -static double yyyymmdd_to_mjd(double yyyymmdd) { -/************************************************************************ - Date given in YYYY-MM-DD format. -************************************************************************/ - - long year = lround(floor( yyyymmdd / 10000 )); - long month = lround(floor((yyyymmdd - year*10000) / 100)); - long day = lround(floor( yyyymmdd - year*10000 - month*100)); - double mjd = daynumber_in_year(year, month, day); - - for (year -= 1; year > 1858; year--) - mjd += days_in_year(year); - - return mjd + 13 + 31; -} - - -/***********************************************************************/ -static double mjd_to_yyyymmdd(double mjd) { -/************************************************************************ - Date given in YYYY-MM-DD format. -************************************************************************/ - double mjd_iter = 14 + 31; - int year = 1859, month=0, day=0; - - for (; mjd >= mjd_iter; year++) { - mjd_iter += days_in_year(year); - } - year--; - mjd_iter -= days_in_year(year); - - for (month=1; mjd_iter + days_in_month(year, month) <= mjd; month++) - mjd_iter += days_in_month(year, month); - - day = (int)(mjd - mjd_iter + 1); - - return year*10000.0 + month*100.0 + day; -} - -static const struct TIME_UNITS time_units[] = { - {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, - {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, - {"gps_week", gps_week_to_mjd, mjd_to_gps_week, "GPS Week"}, - {"yyyymmdd", yyyymmdd_to_mjd, mjd_to_yyyymmdd, "YYYYMMDD date"}, - {NULL, NULL, NULL, NULL} -}; - - -/***********************************************************************/ -static XY forward_2d(LP lp, PJ *P) { -/************************************************************************ - Forward unit conversions in the plane -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.lp = lp; - - point.xy.x *= Q->xy_factor; - point.xy.y *= Q->xy_factor; - - return point.xy; -} - - -/***********************************************************************/ -static LP reverse_2d(XY xy, PJ *P) { -/************************************************************************ - Reverse unit conversions in the plane -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.xy = xy; - - point.xy.x /= Q->xy_factor; - point.xy.y /= Q->xy_factor; - - return point.lp; -} - - -/***********************************************************************/ -static XYZ forward_3d(LPZ lpz, PJ *P) { -/************************************************************************ - Forward unit conversions the vertical component -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.lpz = lpz; - - /* take care of the horizontal components in the 2D function */ - point.xy = forward_2d(point.lp, P); - - point.xyz.z *= Q->z_factor; - - return point.xyz; -} - -/***********************************************************************/ -static LPZ reverse_3d(XYZ xyz, PJ *P) { -/************************************************************************ - Reverse unit conversions the vertical component -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.xyz = xyz; - - /* take care of the horizontal components in the 2D function */ - point.lp = reverse_2d(point.xy, P); - - point.xyz.z /= Q->z_factor; - - return point.lpz; -} - - -/***********************************************************************/ -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { -/************************************************************************ - Forward conversion of time units -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD out = obs; - - /* delegate unit conversion of physical dimensions to the 3D function */ - out.xyz = forward_3d(obs.lpz, P); - - if (Q->t_in_id >= 0) - out.xyzt.t = time_units[Q->t_in_id].t_in( obs.xyzt.t ); - if (Q->t_out_id >= 0) - out.xyzt.t = time_units[Q->t_out_id].t_out( out.xyzt.t ); - - return out; -} - - -/***********************************************************************/ -static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { -/************************************************************************ - Reverse conversion of time units -************************************************************************/ - struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; - PJ_COORD out = obs; - - /* delegate unit conversion of physical dimensions to the 3D function */ - out.lpz = reverse_3d(obs.xyz, P); - - if (Q->t_out_id >= 0) - out.xyzt.t = time_units[Q->t_out_id].t_in( obs.xyzt.t ); - if (Q->t_in_id >= 0) - out.xyzt.t = time_units[Q->t_in_id].t_out( out.xyzt.t ); - - return out; -} - -/***********************************************************************/ -static double get_unit_conversion_factor(const char* name, - int* p_is_linear, - const char** p_normalized_name) { -/***********************************************************************/ - int i; - const char* s; - const PJ_UNITS *units; - - units = proj_list_units(); - - /* Try first with linear units */ - for (i = 0; (s = units[i].id) ; ++i) { - if ( strcmp(s, name) == 0 ) { - if( p_normalized_name ) { - *p_normalized_name = units[i].name; - } - if( p_is_linear ) { - *p_is_linear = 1; - } - return units[i].factor; - } - } - - /* And then angular units */ - units = proj_list_angular_units(); - for (i = 0; (s = units[i].id) ; ++i) { - if ( strcmp(s, name) == 0 ) { - if( p_normalized_name ) { - *p_normalized_name = units[i].name; - } - if( p_is_linear ) { - *p_is_linear = 0; - } - return units[i].factor; - } - } - if( p_normalized_name ) { - *p_normalized_name = NULL; - } - if( p_is_linear ) { - *p_is_linear = -1; - } - return 0.0; -} - -/***********************************************************************/ -PJ *CONVERSION(unitconvert,0) { -/***********************************************************************/ - struct pj_opaque_unitconvert *Q = pj_calloc (1, sizeof (struct pj_opaque_unitconvert)); - char *s, *name; - int i; - double f; - int xy_in_is_linear = -1; /* unknown */ - int xy_out_is_linear = -1; /* unknown */ - int z_in_is_linear = -1; /* unknown */ - int z_out_is_linear = -1; /* unknown */ - - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = (void *) Q; - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = forward_2d; - P->inv = reverse_2d; - - P->left = PJ_IO_UNITS_WHATEVER; - P->right = PJ_IO_UNITS_WHATEVER; - - /* if no time input/output unit is specified we can skip them */ - Q->t_in_id = -1; - Q->t_out_id = -1; - - Q->xy_factor = 1.0; - Q->z_factor = 1.0; - - if ((name = pj_param (P->ctx, P->params, "sxy_in").s) != NULL) { - const char* normalized_name = NULL; - f = get_unit_conversion_factor(name, &xy_in_is_linear, &normalized_name); - if (f != 0.0) { - proj_log_debug(P, "xy_in unit: %s", normalized_name); - } else { - if ( (f = pj_param (P->ctx, P->params, "dxy_in").f) == 0.0) - return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); - } - if (f != 0.0) - Q->xy_factor *= f; - } - - if ((name = pj_param (P->ctx, P->params, "sxy_out").s) != NULL) { - const char* normalized_name = NULL; - f = get_unit_conversion_factor(name, &xy_out_is_linear, &normalized_name); - if (f != 0.0) { - proj_log_debug(P, "xy_out unit: %s", normalized_name); - } else { - if ( (f = pj_param (P->ctx, P->params, "dxy_out").f) == 0.0) - return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); - } - if (f != 0.0) - Q->xy_factor /= f; - } - - if( xy_in_is_linear >= 0 && xy_out_is_linear >= 0 && - xy_in_is_linear != xy_out_is_linear ) { - proj_log_debug(P, "inconsistent unit type between xy_in and xy_out"); - return pj_default_destructor(P, PJD_ERR_INCONSISTENT_UNIT); - } - - if ((name = pj_param (P->ctx, P->params, "sz_in").s) != NULL) { - const char* normalized_name = NULL; - f = get_unit_conversion_factor(name, &z_in_is_linear, &normalized_name); - if (f != 0.0) { - proj_log_debug(P, "z_in unit: %s", normalized_name); - } else { - if ( (f = pj_param (P->ctx, P->params, "dz_in").f) == 0.0) - return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); - } - if (f != 0.0) - Q->z_factor *= f; - } - - if ((name = pj_param (P->ctx, P->params, "sz_out").s) != NULL) { - const char* normalized_name = NULL; - f = get_unit_conversion_factor(name, &z_out_is_linear, &normalized_name); - if (f != 0.0) { - proj_log_debug(P, "z_out unit: %s", normalized_name); - } else { - if ( (f = pj_param (P->ctx, P->params, "dz_out").f) == 0.0) - return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); - } - if (f != 0.0) - Q->z_factor /= f; - } - - if( z_in_is_linear >= 0 && z_out_is_linear >= 0 && - z_in_is_linear != z_out_is_linear ) { - proj_log_debug(P, "inconsistent unit type between z_in and z_out"); - return pj_default_destructor(P, PJD_ERR_INCONSISTENT_UNIT); - } - - if ((name = pj_param (P->ctx, P->params, "st_in").s) != NULL) { - for (i = 0; (s = time_units[i].id) && strcmp(name, s) ; ++i); - - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ - - Q->t_in_id = i; - proj_log_debug(P, "t_in unit: %s", time_units[i].name); - } - - s = 0; - if ((name = pj_param (P->ctx, P->params, "st_out").s) != NULL) { - for (i = 0; (s = time_units[i].id) && strcmp(name, s) ; ++i); - - if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ - - Q->t_out_id = i; - proj_log_debug(P, "t_out unit: %s", time_units[i].name); - } - - return P; -} diff --git a/src/PJ_unitconvert.cpp b/src/PJ_unitconvert.cpp new file mode 100644 index 00000000..7476620e --- /dev/null +++ b/src/PJ_unitconvert.cpp @@ -0,0 +1,548 @@ +/*********************************************************************** + + Unit conversion pseudo-projection for use with + transformation pipelines. + + Kristian Evers, 2017-05-16 + +************************************************************************ + +A pseudo-projection that can be used to convert units of input and +output data. Primarily useful in pipelines. + +Unit conversion is performed by means of a pivot unit. The pivot unit +for distance units are the meter and for time we use the modified julian +date. A time unit conversion is performed like + + Unit A -> Modified Julian date -> Unit B + +distance units are converted in the same manner, with meter being the +central unit. + +The modified Julian date is chosen as the pivot unit since it has a +fairly high precision, goes sufficiently long backwards in time, has no +danger of hitting the upper limit in the near future and it is a fairly +common time unit in astronomy and geodesy. Note that we are using the +Julian date and not day. The difference being that the latter is defined +as an integer and is thus limited to days in resolution. This approach +has been extended wherever it makes sense, e.g. the GPS week unit also +has a fractional part that makes it possible to determine the day, hour +and minute of an observation. + +In- and output units are controlled with the parameters + + +xy_in, +xy_out, +z_in, +z_out, +t_in and +t_out + +where xy denotes horizontal units, z vertical units and t time units. + +************************************************************************ + +Kristian Evers, kreve@sdfe.dk, 2017-05-09 +Last update: 2017-05-16 + +************************************************************************ +* Copyright (c) 2017, Kristian Evers / SDFE +* +* 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. +* +***********************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +PROJ_HEAD(unitconvert, "Unit conversion"); + +typedef double (*tconvert)(double); + +struct TIME_UNITS { + char *id; /* units keyword */ + tconvert t_in; /* unit -> mod. julian date function pointer */ + tconvert t_out; /* mod. julian date > unit function pointer */ + char *name; /* comments */ +}; + +struct pj_opaque_unitconvert { + int t_in_id; /* time unit id for the time input unit */ + int t_out_id; /* time unit id for the time output unit */ + double xy_factor; /* unit conversion factor for horizontal components */ + double z_factor; /* unit conversion factor for vertical components */ +}; + + +/***********************************************************************/ +static int is_leap_year(long year) { +/***********************************************************************/ + return ((year % 4 == 0 && year % 100 != 0) || year % 400 ==0); +} + + +/***********************************************************************/ +static int days_in_year(long year) { +/***********************************************************************/ + return is_leap_year(year) ? 366 : 365; +} + +/***********************************************************************/ +static unsigned int days_in_month(unsigned long year, unsigned long month) { +/***********************************************************************/ + const unsigned int month_table[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + unsigned int days; + + if (month > 12) month = 12; + if (month == 0) month = 1; + + days = month_table[month-1]; + if (is_leap_year(year) && month == 2) days++; + + return days; +} + + +/***********************************************************************/ +static int daynumber_in_year(unsigned long year, unsigned long month, unsigned long day) { +/***********************************************************************/ + unsigned int daynumber=0, i; + + if (month > 12) month = 12; + if (month == 0) month = 1; + if (day > days_in_month(year, month)) day = days_in_month(year, month); + + for (i = 1; i < month; i++) + daynumber += days_in_month(year, i); + + daynumber += day; + + return daynumber; + +} + +/***********************************************************************/ +static double mjd_to_mjd(double mjd) { +/*********************************************************************** + Modified julian date no-op function. + + The Julian date is defined as (fractional) days since midnight + on 16th of November in 1858. +************************************************************************/ + return mjd; +} + + +/***********************************************************************/ +static double decimalyear_to_mjd(double decimalyear) { +/*********************************************************************** + Epoch of modified julian date is 1858-11-16 00:00 +************************************************************************/ + long year; + double fractional_year; + double mjd; + + if( decimalyear < -10000 || decimalyear > 10000 ) + return 0; + + year = lround(floor(decimalyear)); + fractional_year = decimalyear - year; + mjd = (year - 1859)*365 + 14 + 31; + mjd += (double)fractional_year*(double)days_in_year(year); + + /* take care of leap days */ + year--; + for (; year > 1858; year--) + if (is_leap_year(year)) + mjd++; + + return mjd; +} + + +/***********************************************************************/ +static double mjd_to_decimalyear(double mjd) { +/*********************************************************************** + Epoch of modified julian date is 1858-11-16 00:00 +************************************************************************/ + double decimalyear = mjd; + double mjd_iter = 14 + 31; + int year = 1859; + + /* a smarter brain than mine could probably to do this more elegantly + - I'll just brute-force my way out of this... */ + for (; mjd >= mjd_iter; year++) { + mjd_iter += days_in_year(year); + } + year--; + mjd_iter -= days_in_year(year); + + decimalyear = year + (mjd-mjd_iter)/days_in_year(year); + return decimalyear; +} + + +/***********************************************************************/ +static double gps_week_to_mjd(double gps_week) { +/*********************************************************************** + GPS weeks are defined as the number of weeks since January the 6th + 1980. + + Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian + date is 44244. +************************************************************************/ + return 44244.0 + gps_week*7.0; +} + + +/***********************************************************************/ +static double mjd_to_gps_week(double mjd) { +/*********************************************************************** + GPS weeks are defined as the number of weeks since January the 6th + 1980. + + Epoch of gps weeks is 1980-01-06 00:00, which in modified Julian + date is 44244. +************************************************************************/ + return (mjd - 44244.0) / 7.0; +} + + +/***********************************************************************/ +static double yyyymmdd_to_mjd(double yyyymmdd) { +/************************************************************************ + Date given in YYYY-MM-DD format. +************************************************************************/ + + long year = lround(floor( yyyymmdd / 10000 )); + long month = lround(floor((yyyymmdd - year*10000) / 100)); + long day = lround(floor( yyyymmdd - year*10000 - month*100)); + double mjd = daynumber_in_year(year, month, day); + + for (year -= 1; year > 1858; year--) + mjd += days_in_year(year); + + return mjd + 13 + 31; +} + + +/***********************************************************************/ +static double mjd_to_yyyymmdd(double mjd) { +/************************************************************************ + Date given in YYYY-MM-DD format. +************************************************************************/ + double mjd_iter = 14 + 31; + int year = 1859, month=0, day=0; + + for (; mjd >= mjd_iter; year++) { + mjd_iter += days_in_year(year); + } + year--; + mjd_iter -= days_in_year(year); + + for (month=1; mjd_iter + days_in_month(year, month) <= mjd; month++) + mjd_iter += days_in_month(year, month); + + day = (int)(mjd - mjd_iter + 1); + + return year*10000.0 + month*100.0 + day; +} + +static const struct TIME_UNITS time_units[] = { + {"mjd", mjd_to_mjd, mjd_to_mjd, "Modified julian date"}, + {"decimalyear", decimalyear_to_mjd, mjd_to_decimalyear, "Decimal year"}, + {"gps_week", gps_week_to_mjd, mjd_to_gps_week, "GPS Week"}, + {"yyyymmdd", yyyymmdd_to_mjd, mjd_to_yyyymmdd, "YYYYMMDD date"}, + {NULL, NULL, NULL, NULL} +}; + + +/***********************************************************************/ +static XY forward_2d(LP lp, PJ *P) { +/************************************************************************ + Forward unit conversions in the plane +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.lp = lp; + + point.xy.x *= Q->xy_factor; + point.xy.y *= Q->xy_factor; + + return point.xy; +} + + +/***********************************************************************/ +static LP reverse_2d(XY xy, PJ *P) { +/************************************************************************ + Reverse unit conversions in the plane +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.xy = xy; + + point.xy.x /= Q->xy_factor; + point.xy.y /= Q->xy_factor; + + return point.lp; +} + + +/***********************************************************************/ +static XYZ forward_3d(LPZ lpz, PJ *P) { +/************************************************************************ + Forward unit conversions the vertical component +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.lpz = lpz; + + /* take care of the horizontal components in the 2D function */ + point.xy = forward_2d(point.lp, P); + + point.xyz.z *= Q->z_factor; + + return point.xyz; +} + +/***********************************************************************/ +static LPZ reverse_3d(XYZ xyz, PJ *P) { +/************************************************************************ + Reverse unit conversions the vertical component +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.xyz = xyz; + + /* take care of the horizontal components in the 2D function */ + point.lp = reverse_2d(point.xy, P); + + point.xyz.z /= Q->z_factor; + + return point.lpz; +} + + +/***********************************************************************/ +static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { +/************************************************************************ + Forward conversion of time units +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD out = obs; + + /* delegate unit conversion of physical dimensions to the 3D function */ + out.xyz = forward_3d(obs.lpz, P); + + if (Q->t_in_id >= 0) + out.xyzt.t = time_units[Q->t_in_id].t_in( obs.xyzt.t ); + if (Q->t_out_id >= 0) + out.xyzt.t = time_units[Q->t_out_id].t_out( out.xyzt.t ); + + return out; +} + + +/***********************************************************************/ +static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { +/************************************************************************ + Reverse conversion of time units +************************************************************************/ + struct pj_opaque_unitconvert *Q = (struct pj_opaque_unitconvert *) P->opaque; + PJ_COORD out = obs; + + /* delegate unit conversion of physical dimensions to the 3D function */ + out.lpz = reverse_3d(obs.xyz, P); + + if (Q->t_out_id >= 0) + out.xyzt.t = time_units[Q->t_out_id].t_in( obs.xyzt.t ); + if (Q->t_in_id >= 0) + out.xyzt.t = time_units[Q->t_in_id].t_out( out.xyzt.t ); + + return out; +} + +/***********************************************************************/ +static double get_unit_conversion_factor(const char* name, + int* p_is_linear, + const char** p_normalized_name) { +/***********************************************************************/ + int i; + const char* s; + const PJ_UNITS *units; + + units = proj_list_units(); + + /* Try first with linear units */ + for (i = 0; (s = units[i].id) ; ++i) { + if ( strcmp(s, name) == 0 ) { + if( p_normalized_name ) { + *p_normalized_name = units[i].name; + } + if( p_is_linear ) { + *p_is_linear = 1; + } + return units[i].factor; + } + } + + /* And then angular units */ + units = proj_list_angular_units(); + for (i = 0; (s = units[i].id) ; ++i) { + if ( strcmp(s, name) == 0 ) { + if( p_normalized_name ) { + *p_normalized_name = units[i].name; + } + if( p_is_linear ) { + *p_is_linear = 0; + } + return units[i].factor; + } + } + if( p_normalized_name ) { + *p_normalized_name = NULL; + } + if( p_is_linear ) { + *p_is_linear = -1; + } + return 0.0; +} + +/***********************************************************************/ +PJ *CONVERSION(unitconvert,0) { +/***********************************************************************/ + struct pj_opaque_unitconvert *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque_unitconvert))); + char *s, *name; + int i; + double f; + int xy_in_is_linear = -1; /* unknown */ + int xy_out_is_linear = -1; /* unknown */ + int z_in_is_linear = -1; /* unknown */ + int z_out_is_linear = -1; /* unknown */ + + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = (void *) Q; + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = forward_2d; + P->inv = reverse_2d; + + P->left = PJ_IO_UNITS_WHATEVER; + P->right = PJ_IO_UNITS_WHATEVER; + + /* if no time input/output unit is specified we can skip them */ + Q->t_in_id = -1; + Q->t_out_id = -1; + + Q->xy_factor = 1.0; + Q->z_factor = 1.0; + + if ((name = pj_param (P->ctx, P->params, "sxy_in").s) != NULL) { + const char* normalized_name = NULL; + f = get_unit_conversion_factor(name, &xy_in_is_linear, &normalized_name); + if (f != 0.0) { + proj_log_debug(P, "xy_in unit: %s", normalized_name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dxy_in").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); + } + if (f != 0.0) + Q->xy_factor *= f; + } + + if ((name = pj_param (P->ctx, P->params, "sxy_out").s) != NULL) { + const char* normalized_name = NULL; + f = get_unit_conversion_factor(name, &xy_out_is_linear, &normalized_name); + if (f != 0.0) { + proj_log_debug(P, "xy_out unit: %s", normalized_name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dxy_out").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); + } + if (f != 0.0) + Q->xy_factor /= f; + } + + if( xy_in_is_linear >= 0 && xy_out_is_linear >= 0 && + xy_in_is_linear != xy_out_is_linear ) { + proj_log_debug(P, "inconsistent unit type between xy_in and xy_out"); + return pj_default_destructor(P, PJD_ERR_INCONSISTENT_UNIT); + } + + if ((name = pj_param (P->ctx, P->params, "sz_in").s) != NULL) { + const char* normalized_name = NULL; + f = get_unit_conversion_factor(name, &z_in_is_linear, &normalized_name); + if (f != 0.0) { + proj_log_debug(P, "z_in unit: %s", normalized_name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dz_in").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); + } + if (f != 0.0) + Q->z_factor *= f; + } + + if ((name = pj_param (P->ctx, P->params, "sz_out").s) != NULL) { + const char* normalized_name = NULL; + f = get_unit_conversion_factor(name, &z_out_is_linear, &normalized_name); + if (f != 0.0) { + proj_log_debug(P, "z_out unit: %s", normalized_name); + } else { + if ( (f = pj_param (P->ctx, P->params, "dz_out").f) == 0.0) + return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); + } + if (f != 0.0) + Q->z_factor /= f; + } + + if( z_in_is_linear >= 0 && z_out_is_linear >= 0 && + z_in_is_linear != z_out_is_linear ) { + proj_log_debug(P, "inconsistent unit type between z_in and z_out"); + return pj_default_destructor(P, PJD_ERR_INCONSISTENT_UNIT); + } + + if ((name = pj_param (P->ctx, P->params, "st_in").s) != NULL) { + for (i = 0; (s = time_units[i].id) && strcmp(name, s) ; ++i); + + if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ + + Q->t_in_id = i; + proj_log_debug(P, "t_in unit: %s", time_units[i].name); + } + + s = 0; + if ((name = pj_param (P->ctx, P->params, "st_out").s) != NULL) { + for (i = 0; (s = time_units[i].id) && strcmp(name, s) ; ++i); + + if (!s) return pj_default_destructor(P, PJD_ERR_UNKNOWN_UNIT_ID); /* unknown unit conversion id */ + + Q->t_out_id = i; + proj_log_debug(P, "t_out unit: %s", time_units[i].name); + } + + return P; +} diff --git a/src/PJ_urm5.c b/src/PJ_urm5.c deleted file mode 100644 index 293de8cf..00000000 --- a/src/PJ_urm5.c +++ /dev/null @@ -1,54 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(urm5, "Urmaev V") "\n\tPCyl, Sph, no inv\n\tn= q= alpha="; - -struct pj_opaque { - double m, rmn, q3, n; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - struct pj_opaque *Q = P->opaque; - double t; - - t = lp.phi = aasin (P->ctx, Q->n * sin (lp.phi)); - xy.x = Q->m * lp.lam * cos (lp.phi); - t *= t; - xy.y = lp.phi * (1. + t * Q->q3) * Q->rmn; - return xy; -} - - -PJ *PROJECTION(urm5) { - double alpha, t; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - if (pj_param(P->ctx, P->params, "tn").i) { - Q->n = pj_param(P->ctx, P->params, "dn").f; - if (Q->n <= 0. || Q->n > 1.) - return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); - } else { - return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); - } - Q->q3 = pj_param(P->ctx, P->params, "dq").f / 3.; - alpha = pj_param(P->ctx, P->params, "ralpha").f; - t = Q->n * sin (alpha); - Q->m = cos (alpha) / sqrt (1. - t * t); - Q->rmn = 1. / (Q->m * Q->n); - - P->es = 0.; - P->inv = 0; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_urm5.cpp b/src/PJ_urm5.cpp new file mode 100644 index 00000000..6a208647 --- /dev/null +++ b/src/PJ_urm5.cpp @@ -0,0 +1,54 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(urm5, "Urmaev V") "\n\tPCyl, Sph, no inv\n\tn= q= alpha="; + +struct pj_opaque { + double m, rmn, q3, n; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double t; + + t = lp.phi = aasin (P->ctx, Q->n * sin (lp.phi)); + xy.x = Q->m * lp.lam * cos (lp.phi); + t *= t; + xy.y = lp.phi * (1. + t * Q->q3) * Q->rmn; + return xy; +} + + +PJ *PROJECTION(urm5) { + double alpha, t; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + if (pj_param(P->ctx, P->params, "tn").i) { + Q->n = pj_param(P->ctx, P->params, "dn").f; + if (Q->n <= 0. || Q->n > 1.) + return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); + } else { + return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); + } + Q->q3 = pj_param(P->ctx, P->params, "dq").f / 3.; + alpha = pj_param(P->ctx, P->params, "ralpha").f; + t = Q->n * sin (alpha); + Q->m = cos (alpha) / sqrt (1. - t * t); + Q->rmn = 1. / (Q->m * Q->n); + + P->es = 0.; + P->inv = 0; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_urmfps.c b/src/PJ_urmfps.c deleted file mode 100644 index 467fc9bc..00000000 --- a/src/PJ_urmfps.c +++ /dev/null @@ -1,74 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(urmfps, "Urmaev Flat-Polar Sinusoidal") "\n\tPCyl, Sph\n\tn="; -PROJ_HEAD(wag1, "Wagner I (Kavraisky VI)") "\n\tPCyl, Sph"; - -struct pj_opaque { - double n, C_y; -}; - -#define C_x 0.8773826753 -#define Cy 1.139753528477 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - lp.phi = aasin (P->ctx,P->opaque->n * sin (lp.phi)); - xy.x = C_x * lp.lam * cos (lp.phi); - xy.y = P->opaque->C_y * lp.phi; - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0, 0.0}; - xy.y /= P->opaque->C_y; - lp.phi = aasin(P->ctx, sin (xy.y) / P->opaque->n); - lp.lam = xy.x / (C_x * cos (xy.y)); - return lp; -} - - -static PJ *setup(PJ *P) { - P->opaque->C_y = Cy / P->opaque->n; - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} - - -PJ *PROJECTION(urmfps) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - - P->opaque = Q; - - if (pj_param(P->ctx, P->params, "tn").i) { - P->opaque->n = pj_param(P->ctx, P->params, "dn").f; - if (P->opaque->n <= 0. || P->opaque->n > 1.) - return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); - } else { - return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); - } - - return setup(P); -} - - -PJ *PROJECTION(wag1) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - P->opaque->n = 0.8660254037844386467637231707; - return setup(P); -} diff --git a/src/PJ_urmfps.cpp b/src/PJ_urmfps.cpp new file mode 100644 index 00000000..1d147b9c --- /dev/null +++ b/src/PJ_urmfps.cpp @@ -0,0 +1,74 @@ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(urmfps, "Urmaev Flat-Polar Sinusoidal") "\n\tPCyl, Sph\n\tn="; +PROJ_HEAD(wag1, "Wagner I (Kavraisky VI)") "\n\tPCyl, Sph"; + +struct pj_opaque { + double n, C_y; +}; + +#define C_x 0.8773826753 +#define Cy 1.139753528477 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + lp.phi = aasin (P->ctx,static_cast(P->opaque)->n * sin (lp.phi)); + xy.x = C_x * lp.lam * cos (lp.phi); + xy.y = static_cast(P->opaque)->C_y * lp.phi; + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + xy.y /= static_cast(P->opaque)->C_y; + lp.phi = aasin(P->ctx, sin (xy.y) / static_cast(P->opaque)->n); + lp.lam = xy.x / (C_x * cos (xy.y)); + return lp; +} + + +static PJ *setup(PJ *P) { + static_cast(P->opaque)->C_y = Cy / static_cast(P->opaque)->n; + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(urmfps) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + + P->opaque = Q; + + if (pj_param(P->ctx, P->params, "tn").i) { + static_cast(P->opaque)->n = pj_param(P->ctx, P->params, "dn").f; + if (static_cast(P->opaque)->n <= 0. || static_cast(P->opaque)->n > 1.) + return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); + } else { + return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE); + } + + return setup(P); +} + + +PJ *PROJECTION(wag1) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + static_cast(P->opaque)->n = 0.8660254037844386467637231707; + return setup(P); +} diff --git a/src/PJ_vandg.c b/src/PJ_vandg.c deleted file mode 100644 index d148e210..00000000 --- a/src/PJ_vandg.c +++ /dev/null @@ -1,106 +0,0 @@ -#define PJ_LIB__ -#include "proj.h" -#include "projects.h" - -PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph"; - -# define TOL 1.e-10 -# define THIRD .33333333333333333333 -# define C2_27 .07407407407407407407 -# define PI4_3 4.18879020478639098458 -# define PISQ 9.86960440108935861869 -# define TPISQ 19.73920880217871723738 -# define HPISQ 4.93480220054467930934 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double al, al2, g, g2, p2; - - p2 = fabs(lp.phi / M_HALFPI); - if ((p2 - TOL) > 1.) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - if (p2 > 1.) - p2 = 1.; - if (fabs(lp.phi) <= TOL) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) { - xy.x = 0.; - xy.y = M_PI * tan(.5 * asin(p2)); - if (lp.phi < 0.) xy.y = -xy.y; - } else { - al = .5 * fabs(M_PI / lp.lam - lp.lam / M_PI); - al2 = al * al; - g = sqrt(1. - p2 * p2); - g = g / (p2 + g - 1.); - g2 = g * g; - p2 = g * (2. / p2 - 1.); - p2 = p2 * p2; - xy.x = g - p2; g = p2 + al2; - xy.x = M_PI * (al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g; - if (lp.lam < 0.) xy.x = -xy.x; - xy.y = fabs(xy.x / M_PI); - xy.y = 1. - xy.y * (xy.y + 2. * al); - if (xy.y < -TOL) { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return xy; - } - if (xy.y < 0.) - xy.y = 0.; - else - xy.y = sqrt(xy.y) * (lp.phi < 0. ? -M_PI : M_PI); - } - - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2; - - x2 = xy.x * xy.x; - if ((ay = fabs(xy.y)) < TOL) { - lp.phi = 0.; - t = x2 * x2 + TPISQ * (x2 + HPISQ); - lp.lam = fabs(xy.x) <= TOL ? 0. : - .5 * (x2 - PISQ + sqrt(t)) / xy.x; - return (lp); - } - y2 = xy.y * xy.y; - r = x2 + y2; r2 = r * r; - c1 = - M_PI * ay * (r + PISQ); - c3 = r2 + M_TWOPI * (ay * r + M_PI * (y2 + M_PI * (ay + M_HALFPI))); - c2 = c1 + PISQ * (r - 3. * y2); - c0 = M_PI * ay; - c2 /= c3; - al = c1 / c3 - THIRD * c2 * c2; - m = 2. * sqrt(-THIRD * al); - d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; - if (((t = fabs(d = 3. * d / (al * m))) - TOL) <= 1.) { - d = t > 1. ? (d > 0. ? 0. : M_PI) : acos(d); - lp.phi = M_PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2); - if (xy.y < 0.) lp.phi = -lp.phi; - t = r2 + TPISQ * (x2 - y2 + HPISQ); - lp.lam = fabs(xy.x) <= TOL ? 0. : - .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x; - } else { - proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); - return lp; - } - - return lp; -} - - -PJ *PROJECTION(vandg) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} - diff --git a/src/PJ_vandg.cpp b/src/PJ_vandg.cpp new file mode 100644 index 00000000..d148e210 --- /dev/null +++ b/src/PJ_vandg.cpp @@ -0,0 +1,106 @@ +#define PJ_LIB__ +#include "proj.h" +#include "projects.h" + +PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph"; + +# define TOL 1.e-10 +# define THIRD .33333333333333333333 +# define C2_27 .07407407407407407407 +# define PI4_3 4.18879020478639098458 +# define PISQ 9.86960440108935861869 +# define TPISQ 19.73920880217871723738 +# define HPISQ 4.93480220054467930934 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double al, al2, g, g2, p2; + + p2 = fabs(lp.phi / M_HALFPI); + if ((p2 - TOL) > 1.) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + if (p2 > 1.) + p2 = 1.; + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = 0.; + } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) { + xy.x = 0.; + xy.y = M_PI * tan(.5 * asin(p2)); + if (lp.phi < 0.) xy.y = -xy.y; + } else { + al = .5 * fabs(M_PI / lp.lam - lp.lam / M_PI); + al2 = al * al; + g = sqrt(1. - p2 * p2); + g = g / (p2 + g - 1.); + g2 = g * g; + p2 = g * (2. / p2 - 1.); + p2 = p2 * p2; + xy.x = g - p2; g = p2 + al2; + xy.x = M_PI * (al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g; + if (lp.lam < 0.) xy.x = -xy.x; + xy.y = fabs(xy.x / M_PI); + xy.y = 1. - xy.y * (xy.y + 2. * al); + if (xy.y < -TOL) { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return xy; + } + if (xy.y < 0.) + xy.y = 0.; + else + xy.y = sqrt(xy.y) * (lp.phi < 0. ? -M_PI : M_PI); + } + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2; + + x2 = xy.x * xy.x; + if ((ay = fabs(xy.y)) < TOL) { + lp.phi = 0.; + t = x2 * x2 + TPISQ * (x2 + HPISQ); + lp.lam = fabs(xy.x) <= TOL ? 0. : + .5 * (x2 - PISQ + sqrt(t)) / xy.x; + return (lp); + } + y2 = xy.y * xy.y; + r = x2 + y2; r2 = r * r; + c1 = - M_PI * ay * (r + PISQ); + c3 = r2 + M_TWOPI * (ay * r + M_PI * (y2 + M_PI * (ay + M_HALFPI))); + c2 = c1 + PISQ * (r - 3. * y2); + c0 = M_PI * ay; + c2 /= c3; + al = c1 / c3 - THIRD * c2 * c2; + m = 2. * sqrt(-THIRD * al); + d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; + if (((t = fabs(d = 3. * d / (al * m))) - TOL) <= 1.) { + d = t > 1. ? (d > 0. ? 0. : M_PI) : acos(d); + lp.phi = M_PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2); + if (xy.y < 0.) lp.phi = -lp.phi; + t = r2 + TPISQ * (x2 - y2 + HPISQ); + lp.lam = fabs(xy.x) <= TOL ? 0. : + .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x; + } else { + proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION); + return lp; + } + + return lp; +} + + +PJ *PROJECTION(vandg) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + diff --git a/src/PJ_vandg2.c b/src/PJ_vandg2.c deleted file mode 100644 index 447cb782..00000000 --- a/src/PJ_vandg2.c +++ /dev/null @@ -1,74 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -struct pj_opaque { - int vdg3; -}; - -PROJ_HEAD(vandg2, "van der Grinten II") "\n\tMisc Sph, no inv"; -PROJ_HEAD(vandg3, "van der Grinten III") "\n\tMisc Sph, no inv"; - -#define TOL 1e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double x1, at, bt, ct; - - bt = fabs(M_TWO_D_PI * lp.phi); - if ((ct = 1. - bt * bt) < 0.) - ct = 0.; - else - ct = sqrt(ct); - if (fabs(lp.lam) < TOL) { - xy.x = 0.; - xy.y = M_PI * (lp.phi < 0. ? -bt : bt) / (1. + ct); - } else { - at = 0.5 * fabs(M_PI / lp.lam - lp.lam / M_PI); - if (Q->vdg3) { - x1 = bt / (1. + ct); - xy.x = M_PI * (sqrt(at * at + 1. - x1 * x1) - at); - xy.y = M_PI * x1; - } else { - x1 = (ct * sqrt(1. + at * at) - at * ct * ct) / - (1. + at * at * bt * bt); - xy.x = M_PI * x1; - xy.y = M_PI * sqrt(1. - x1 * (x1 + 2. * at) + TOL); - } - if ( lp.lam < 0.) xy.x = -xy.x; - if ( lp.phi < 0.) xy.y = -xy.y; - } - - return xy; -} - - -PJ *PROJECTION(vandg2) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->vdg3 = 0; - P->fwd = s_forward; - - return P; -} - -PJ *PROJECTION(vandg3) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - Q->vdg3 = 1; - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_vandg2.cpp b/src/PJ_vandg2.cpp new file mode 100644 index 00000000..588366cf --- /dev/null +++ b/src/PJ_vandg2.cpp @@ -0,0 +1,74 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +struct pj_opaque { + int vdg3; +}; + +PROJ_HEAD(vandg2, "van der Grinten II") "\n\tMisc Sph, no inv"; +PROJ_HEAD(vandg3, "van der Grinten III") "\n\tMisc Sph, no inv"; + +#define TOL 1e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double x1, at, bt, ct; + + bt = fabs(M_TWO_D_PI * lp.phi); + if ((ct = 1. - bt * bt) < 0.) + ct = 0.; + else + ct = sqrt(ct); + if (fabs(lp.lam) < TOL) { + xy.x = 0.; + xy.y = M_PI * (lp.phi < 0. ? -bt : bt) / (1. + ct); + } else { + at = 0.5 * fabs(M_PI / lp.lam - lp.lam / M_PI); + if (Q->vdg3) { + x1 = bt / (1. + ct); + xy.x = M_PI * (sqrt(at * at + 1. - x1 * x1) - at); + xy.y = M_PI * x1; + } else { + x1 = (ct * sqrt(1. + at * at) - at * ct * ct) / + (1. + at * at * bt * bt); + xy.x = M_PI * x1; + xy.y = M_PI * sqrt(1. - x1 * (x1 + 2. * at) + TOL); + } + if ( lp.lam < 0.) xy.x = -xy.x; + if ( lp.phi < 0.) xy.y = -xy.y; + } + + return xy; +} + + +PJ *PROJECTION(vandg2) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->vdg3 = 0; + P->fwd = s_forward; + + return P; +} + +PJ *PROJECTION(vandg3) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + Q->vdg3 = 1; + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_vandg4.c b/src/PJ_vandg4.c deleted file mode 100644 index d9a53c87..00000000 --- a/src/PJ_vandg4.c +++ /dev/null @@ -1,55 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(vandg4, "van der Grinten IV") "\n\tMisc Sph, no inv"; - -#define TOL 1e-10 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - double x1, t, bt, ct, ft, bt2, ct2, dt, dt2; - (void) P; - - if (fabs(lp.phi) < TOL) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(lp.lam) < TOL || fabs(fabs(lp.phi) - M_HALFPI) < TOL) { - xy.x = 0.; - xy.y = lp.phi; - } else { - bt = fabs(M_TWO_D_PI * lp.phi); - bt2 = bt * bt; - ct = 0.5 * (bt * (8. - bt * (2. + bt2)) - 5.) - / (bt2 * (bt - 1.)); - ct2 = ct * ct; - dt = M_TWO_D_PI * lp.lam; - dt = dt + 1. / dt; - dt = sqrt(dt * dt - 4.); - if ((fabs(lp.lam) - M_HALFPI) < 0.) dt = -dt; - dt2 = dt * dt; - x1 = bt + ct; x1 *= x1; - t = bt + 3.*ct; - ft = x1 * (bt2 + ct2 * dt2 - 1.) + (1.-bt2) * ( - bt2 * (t * t + 4. * ct2) + - ct2 * (12. * bt * ct + 4. * ct2) ); - x1 = (dt*(x1 + ct2 - 1.) + 2.*sqrt(ft)) / - (4.* x1 + dt2); - xy.x = M_HALFPI * x1; - xy.y = M_HALFPI * sqrt(1. + dt * fabs(x1) - x1 * x1); - if (lp.lam < 0.) xy.x = -xy.x; - if (lp.phi < 0.) xy.y = -xy.y; - } - return xy; -} - - -PJ *PROJECTION(vandg4) { - P->es = 0.; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_vandg4.cpp b/src/PJ_vandg4.cpp new file mode 100644 index 00000000..d9a53c87 --- /dev/null +++ b/src/PJ_vandg4.cpp @@ -0,0 +1,55 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(vandg4, "van der Grinten IV") "\n\tMisc Sph, no inv"; + +#define TOL 1e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double x1, t, bt, ct, ft, bt2, ct2, dt, dt2; + (void) P; + + if (fabs(lp.phi) < TOL) { + xy.x = lp.lam; + xy.y = 0.; + } else if (fabs(lp.lam) < TOL || fabs(fabs(lp.phi) - M_HALFPI) < TOL) { + xy.x = 0.; + xy.y = lp.phi; + } else { + bt = fabs(M_TWO_D_PI * lp.phi); + bt2 = bt * bt; + ct = 0.5 * (bt * (8. - bt * (2. + bt2)) - 5.) + / (bt2 * (bt - 1.)); + ct2 = ct * ct; + dt = M_TWO_D_PI * lp.lam; + dt = dt + 1. / dt; + dt = sqrt(dt * dt - 4.); + if ((fabs(lp.lam) - M_HALFPI) < 0.) dt = -dt; + dt2 = dt * dt; + x1 = bt + ct; x1 *= x1; + t = bt + 3.*ct; + ft = x1 * (bt2 + ct2 * dt2 - 1.) + (1.-bt2) * ( + bt2 * (t * t + 4. * ct2) + + ct2 * (12. * bt * ct + 4. * ct2) ); + x1 = (dt*(x1 + ct2 - 1.) + 2.*sqrt(ft)) / + (4.* x1 + dt2); + xy.x = M_HALFPI * x1; + xy.y = M_HALFPI * sqrt(1. + dt * fabs(x1) - x1 * x1); + if (lp.lam < 0.) xy.x = -xy.x; + if (lp.phi < 0.) xy.y = -xy.y; + } + return xy; +} + + +PJ *PROJECTION(vandg4) { + P->es = 0.; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_vgridshift.c b/src/PJ_vgridshift.c deleted file mode 100644 index ac5d26ef..00000000 --- a/src/PJ_vgridshift.c +++ /dev/null @@ -1,142 +0,0 @@ -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -PROJ_HEAD(vgridshift, "Vertical grid shift"); - -struct pj_opaque_vgridshift { - double t_final; - double t_epoch; - double forward_multiplier; -}; - -static XYZ forward_3d(LPZ lpz, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.lpz = lpz; - - if (P->vgridlist_geoid != NULL) { - /* Only try the gridshift if at least one grid is loaded, - * otherwise just pass the coordinate through unchanged. */ - point.xyz.z += Q->forward_multiplier * proj_vgrid_value(P, point.lp); - } - - return point.xyz; -} - - -static LPZ reverse_3d(XYZ xyz, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; - PJ_COORD point = {{0,0,0,0}}; - point.xyz = xyz; - - if (P->vgridlist_geoid != NULL) { - /* Only try the gridshift if at least one grid is loaded, - * otherwise just pass the coordinate through unchanged. */ - point.xyz.z -= Q->forward_multiplier * proj_vgrid_value(P, point.lp); - } - - return point.lpz; -} - - -static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; - PJ_COORD point = obs; - - /* If transformation is not time restricted, we always call it */ - if (Q->t_final==0 || Q->t_epoch==0) { - point.xyz = forward_3d (obs.lpz, P); - return point; - } - - /* Time restricted - only apply transform if within time bracket */ - if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) - point.xyz = forward_3d (obs.lpz, P); - - - return point; -} - -static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { - struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; - PJ_COORD point = obs; - - /* If transformation is not time restricted, we always call it */ - if (Q->t_final==0 || Q->t_epoch==0) { - point.lpz = reverse_3d (obs.xyz, P); - return point; - } - - /* Time restricted - only apply transform if within time bracket */ - if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) - point.lpz = reverse_3d (obs.xyz, P); - - return point; -} - - -PJ *TRANSFORMATION(vgridshift,0) { - struct pj_opaque_vgridshift *Q = pj_calloc (1, sizeof (struct pj_opaque_vgridshift)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = (void *) Q; - - if (!pj_param(P->ctx, P->params, "tgrids").i) { - proj_log_error(P, "vgridshift: +grids parameter missing."); - return pj_default_destructor(P, PJD_ERR_NO_ARGS); - } - - /* TODO: Refactor into shared function that can be used */ - /* by both vgridshift and hgridshift */ - if (pj_param(P->ctx, P->params, "tt_final").i) { - Q->t_final = pj_param (P->ctx, P->params, "dt_final").f; - if (Q->t_final == 0) { - /* a number wasn't passed to +t_final, let's see if it was "now" */ - /* and set the time accordingly. */ - if (!strcmp("now", pj_param(P->ctx, P->params, "st_final").s)) { - time_t now; - struct tm *date; - time(&now); - date = localtime(&now); - Q->t_final = 1900.0 + date->tm_year + date->tm_yday/365.0; - } - } - } - - if (pj_param(P->ctx, P->params, "tt_epoch").i) - Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; - - /* historical: the forward direction subtracts the grid offset. */ - Q->forward_multiplier = -1.0; - if (pj_param(P->ctx, P->params, "tmultiplier").i) { - Q->forward_multiplier = pj_param(P->ctx, P->params, "dmultiplier").f; - } - - /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */ - proj_vgrid_init(P, "grids"); - - /* Was gridlist compiled properly? */ - if ( proj_errno(P) ) { - proj_log_error(P, "vgridshift: could not find required grid(s)."); - return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - P->fwd4d = forward_4d; - P->inv4d = reverse_4d; - P->fwd3d = forward_3d; - P->inv3d = reverse_3d; - P->fwd = 0; - P->inv = 0; - - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_ANGULAR; - - return P; -} diff --git a/src/PJ_vgridshift.cpp b/src/PJ_vgridshift.cpp new file mode 100644 index 00000000..8aad3777 --- /dev/null +++ b/src/PJ_vgridshift.cpp @@ -0,0 +1,142 @@ +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +PROJ_HEAD(vgridshift, "Vertical grid shift"); + +struct pj_opaque_vgridshift { + double t_final; + double t_epoch; + double forward_multiplier; +}; + +static XYZ forward_3d(LPZ lpz, PJ *P) { + struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.lpz = lpz; + + if (P->vgridlist_geoid != NULL) { + /* Only try the gridshift if at least one grid is loaded, + * otherwise just pass the coordinate through unchanged. */ + point.xyz.z += Q->forward_multiplier * proj_vgrid_value(P, point.lp); + } + + return point.xyz; +} + + +static LPZ reverse_3d(XYZ xyz, PJ *P) { + struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + PJ_COORD point = {{0,0,0,0}}; + point.xyz = xyz; + + if (P->vgridlist_geoid != NULL) { + /* Only try the gridshift if at least one grid is loaded, + * otherwise just pass the coordinate through unchanged. */ + point.xyz.z -= Q->forward_multiplier * proj_vgrid_value(P, point.lp); + } + + return point.lpz; +} + + +static PJ_COORD forward_4d(PJ_COORD obs, PJ *P) { + struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + PJ_COORD point = obs; + + /* If transformation is not time restricted, we always call it */ + if (Q->t_final==0 || Q->t_epoch==0) { + point.xyz = forward_3d (obs.lpz, P); + return point; + } + + /* Time restricted - only apply transform if within time bracket */ + if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) + point.xyz = forward_3d (obs.lpz, P); + + + return point; +} + +static PJ_COORD reverse_4d(PJ_COORD obs, PJ *P) { + struct pj_opaque_vgridshift *Q = (struct pj_opaque_vgridshift *) P->opaque; + PJ_COORD point = obs; + + /* If transformation is not time restricted, we always call it */ + if (Q->t_final==0 || Q->t_epoch==0) { + point.lpz = reverse_3d (obs.xyz, P); + return point; + } + + /* Time restricted - only apply transform if within time bracket */ + if (obs.lpzt.t < Q->t_epoch && Q->t_final > Q->t_epoch) + point.lpz = reverse_3d (obs.xyz, P); + + return point; +} + + +PJ *TRANSFORMATION(vgridshift,0) { + struct pj_opaque_vgridshift *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque_vgridshift))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = (void *) Q; + + if (!pj_param(P->ctx, P->params, "tgrids").i) { + proj_log_error(P, "vgridshift: +grids parameter missing."); + return pj_default_destructor(P, PJD_ERR_NO_ARGS); + } + + /* TODO: Refactor into shared function that can be used */ + /* by both vgridshift and hgridshift */ + if (pj_param(P->ctx, P->params, "tt_final").i) { + Q->t_final = pj_param (P->ctx, P->params, "dt_final").f; + if (Q->t_final == 0) { + /* a number wasn't passed to +t_final, let's see if it was "now" */ + /* and set the time accordingly. */ + if (!strcmp("now", pj_param(P->ctx, P->params, "st_final").s)) { + time_t now; + struct tm *date; + time(&now); + date = localtime(&now); + Q->t_final = 1900.0 + date->tm_year + date->tm_yday/365.0; + } + } + } + + if (pj_param(P->ctx, P->params, "tt_epoch").i) + Q->t_epoch = pj_param (P->ctx, P->params, "dt_epoch").f; + + /* historical: the forward direction subtracts the grid offset. */ + Q->forward_multiplier = -1.0; + if (pj_param(P->ctx, P->params, "tmultiplier").i) { + Q->forward_multiplier = pj_param(P->ctx, P->params, "dmultiplier").f; + } + + /* Build gridlist. P->vgridlist_geoid can be empty if +grids only ask for optional grids. */ + proj_vgrid_init(P, "grids"); + + /* Was gridlist compiled properly? */ + if ( proj_errno(P) ) { + proj_log_error(P, "vgridshift: could not find required grid(s)."); + return pj_default_destructor(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + P->fwd4d = forward_4d; + P->inv4d = reverse_4d; + P->fwd3d = forward_3d; + P->inv3d = reverse_3d; + P->fwd = 0; + P->inv = 0; + + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_ANGULAR; + + return P; +} diff --git a/src/PJ_wag2.c b/src/PJ_wag2.c deleted file mode 100644 index 1bee737a..00000000 --- a/src/PJ_wag2.c +++ /dev/null @@ -1,38 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(wag2, "Wagner II") "\n\tPCyl, Sph"; - -#define C_x 0.92483 -#define C_y 1.38725 -#define C_p1 0.88022 -#define C_p2 0.88550 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - lp.phi = aasin (P->ctx,C_p1 * sin (C_p2 * lp.phi)); - xy.x = C_x * lp.lam * cos (lp.phi); - xy.y = C_y * lp.phi; - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = xy.y / C_y; - lp.lam = xy.x / (C_x * cos(lp.phi)); - lp.phi = aasin (P->ctx,sin(lp.phi) / C_p1) / C_p2; - return (lp); -} - - -PJ *PROJECTION(wag2) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} diff --git a/src/PJ_wag2.cpp b/src/PJ_wag2.cpp new file mode 100644 index 00000000..1bee737a --- /dev/null +++ b/src/PJ_wag2.cpp @@ -0,0 +1,38 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(wag2, "Wagner II") "\n\tPCyl, Sph"; + +#define C_x 0.92483 +#define C_y 1.38725 +#define C_p1 0.88022 +#define C_p2 0.88550 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + lp.phi = aasin (P->ctx,C_p1 * sin (C_p2 * lp.phi)); + xy.x = C_x * lp.lam * cos (lp.phi); + xy.y = C_y * lp.phi; + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = xy.y / C_y; + lp.lam = xy.x / (C_x * cos(lp.phi)); + lp.phi = aasin (P->ctx,sin(lp.phi) / C_p1) / C_p2; + return (lp); +} + + +PJ *PROJECTION(wag2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} diff --git a/src/PJ_wag3.c b/src/PJ_wag3.c deleted file mode 100644 index ccd19d4d..00000000 --- a/src/PJ_wag3.c +++ /dev/null @@ -1,48 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(wag3, "Wagner III") "\n\tPCyl, Sph\n\tlat_ts="; - -#define TWOTHIRD 0.6666666666666666666667 - -struct pj_opaque { - double C_x; -}; - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - xy.x = P->opaque->C_x * lp.lam * cos(TWOTHIRD * lp.phi); - xy.y = lp.phi; - return xy; -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = xy.y; - lp.lam = xy.x / (P->opaque->C_x * cos(TWOTHIRD * lp.phi)); - return lp; -} - - -PJ *PROJECTION(wag3) { - double ts; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - - P->opaque = Q; - - ts = pj_param (P->ctx, P->params, "rlat_ts").f; - P->opaque->C_x = cos (ts) / cos (2.*ts/3.); - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_wag3.cpp b/src/PJ_wag3.cpp new file mode 100644 index 00000000..0eeb73df --- /dev/null +++ b/src/PJ_wag3.cpp @@ -0,0 +1,48 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(wag3, "Wagner III") "\n\tPCyl, Sph\n\tlat_ts="; + +#define TWOTHIRD 0.6666666666666666666667 + +struct pj_opaque { + double C_x; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = static_cast(P->opaque)->C_x * lp.lam * cos(TWOTHIRD * lp.phi); + xy.y = lp.phi; + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = xy.y; + lp.lam = xy.x / (static_cast(P->opaque)->C_x * cos(TWOTHIRD * lp.phi)); + return lp; +} + + +PJ *PROJECTION(wag3) { + double ts; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + + P->opaque = Q; + + ts = pj_param (P->ctx, P->params, "rlat_ts").f; + static_cast(P->opaque)->C_x = cos (ts) / cos (2.*ts/3.); + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_wag7.c b/src/PJ_wag7.c deleted file mode 100644 index 2009e672..00000000 --- a/src/PJ_wag7.c +++ /dev/null @@ -1,30 +0,0 @@ -#define PJ_LIB__ - -#include - -#include "projects.h" - -PROJ_HEAD(wag7, "Wagner VII") "\n\tMisc Sph, no inv"; - - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - double theta, ct, D; - - (void) P; /* Shut up compiler warnnings about unused P */ - - theta = asin (xy.y = 0.90630778703664996 * sin(lp.phi)); - xy.x = 2.66723 * (ct = cos (theta)) * sin (lp.lam /= 3.); - xy.y *= 1.24104 * (D = 1/(sqrt (0.5 * (1 + ct * cos (lp.lam))))); - xy.x *= D; - return (xy); -} - - -PJ *PROJECTION(wag7) { - P->fwd = s_forward; - P->inv = 0; - P->es = 0.; - return P; -} diff --git a/src/PJ_wag7.cpp b/src/PJ_wag7.cpp new file mode 100644 index 00000000..2009e672 --- /dev/null +++ b/src/PJ_wag7.cpp @@ -0,0 +1,30 @@ +#define PJ_LIB__ + +#include + +#include "projects.h" + +PROJ_HEAD(wag7, "Wagner VII") "\n\tMisc Sph, no inv"; + + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + double theta, ct, D; + + (void) P; /* Shut up compiler warnnings about unused P */ + + theta = asin (xy.y = 0.90630778703664996 * sin(lp.phi)); + xy.x = 2.66723 * (ct = cos (theta)) * sin (lp.lam /= 3.); + xy.y *= 1.24104 * (D = 1/(sqrt (0.5 * (1 + ct * cos (lp.lam))))); + xy.x *= D; + return (xy); +} + + +PJ *PROJECTION(wag7) { + P->fwd = s_forward; + P->inv = 0; + P->es = 0.; + return P; +} diff --git a/src/PJ_wink1.c b/src/PJ_wink1.c deleted file mode 100644 index 865cc6ac..00000000 --- a/src/PJ_wink1.c +++ /dev/null @@ -1,44 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(wink1, "Winkel I") "\n\tPCyl, Sph\n\tlat_ts="; - -struct pj_opaque { - double cosphi1; -}; - - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0,0.0}; - xy.x = .5 * lp.lam * (P->opaque->cosphi1 + cos(lp.phi)); - xy.y = lp.phi; - return (xy); -} - - -static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ - LP lp = {0.0,0.0}; - lp.phi = xy.y; - lp.lam = 2. * xy.x / (P->opaque->cosphi1 + cos(lp.phi)); - return (lp); -} - - -PJ *PROJECTION(wink1) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - P->opaque->cosphi1 = cos (pj_param(P->ctx, P->params, "rlat_ts").f); - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_wink1.cpp b/src/PJ_wink1.cpp new file mode 100644 index 00000000..6640f995 --- /dev/null +++ b/src/PJ_wink1.cpp @@ -0,0 +1,44 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(wink1, "Winkel I") "\n\tPCyl, Sph\n\tlat_ts="; + +struct pj_opaque { + double cosphi1; +}; + + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = .5 * lp.lam * (static_cast(P->opaque)->cosphi1 + cos(lp.phi)); + xy.y = lp.phi; + return (xy); +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = xy.y; + lp.lam = 2. * xy.x / (static_cast(P->opaque)->cosphi1 + cos(lp.phi)); + return (lp); +} + + +PJ *PROJECTION(wink1) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + static_cast(P->opaque)->cosphi1 = cos (pj_param(P->ctx, P->params, "rlat_ts").f); + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} diff --git a/src/PJ_wink2.c b/src/PJ_wink2.c deleted file mode 100644 index f8e9af09..00000000 --- a/src/PJ_wink2.c +++ /dev/null @@ -1,52 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -PROJ_HEAD(wink2, "Winkel II") "\n\tPCyl, Sph, no inv\n\tlat_1="; - -struct pj_opaque { double cosphi1; }; - -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 - - -static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ - XY xy = {0.0, 0.0}; - double k, V; - int i; - - xy.y = lp.phi * M_TWO_D_PI; - k = M_PI * sin (lp.phi); - lp.phi *= 1.8; - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin (lp.phi) - k) / - (1. + cos (lp.phi)); - if (fabs (V) < LOOP_TOL) - break; - } - if (!i) - lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; - else - lp.phi *= 0.5; - xy.x = 0.5 * lp.lam * (cos (lp.phi) + P->opaque->cosphi1); - xy.y = M_FORTPI * (sin (lp.phi) + xy.y); - return xy; -} - - -PJ *PROJECTION(wink2) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - P->opaque->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f); - P->es = 0.; - P->inv = 0; - P->fwd = s_forward; - - return P; -} diff --git a/src/PJ_wink2.cpp b/src/PJ_wink2.cpp new file mode 100644 index 00000000..9da65eaa --- /dev/null +++ b/src/PJ_wink2.cpp @@ -0,0 +1,52 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +PROJ_HEAD(wink2, "Winkel II") "\n\tPCyl, Sph, no inv\n\tlat_1="; + +struct pj_opaque { double cosphi1; }; + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + double k, V; + int i; + + xy.y = lp.phi * M_TWO_D_PI; + k = M_PI * sin (lp.phi); + lp.phi *= 1.8; + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (lp.phi + sin (lp.phi) - k) / + (1. + cos (lp.phi)); + if (fabs (V) < LOOP_TOL) + break; + } + if (!i) + lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI; + else + lp.phi *= 0.5; + xy.x = 0.5 * lp.lam * (cos (lp.phi) + static_cast(P->opaque)->cosphi1); + xy.y = M_FORTPI * (sin (lp.phi) + xy.y); + return xy; +} + + +PJ *PROJECTION(wink2) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + static_cast(P->opaque)->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f); + P->es = 0.; + P->inv = 0; + P->fwd = s_forward; + + return P; +} diff --git a/src/aasincos.c b/src/aasincos.c deleted file mode 100644 index 4f9ea25f..00000000 --- a/src/aasincos.c +++ /dev/null @@ -1,38 +0,0 @@ -/* arc sin, cosine, tan2 and sqrt that will NOT fail */ - -#include - -#include "projects.h" - -#define ONE_TOL 1.00000000000001 -#define ATOL 1e-50 - - double -aasin(projCtx ctx,double v) { - double av; - - if ((av = fabs(v)) >= 1.) { - if (av > ONE_TOL) - pj_ctx_set_errno( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE ); - return (v < 0. ? -M_HALFPI : M_HALFPI); - } - return asin(v); -} - - double -aacos(projCtx ctx, double v) { - double av; - - if ((av = fabs(v)) >= 1.) { - if (av > ONE_TOL) - pj_ctx_set_errno( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE ); - return (v < 0. ? M_PI : 0.); - } - return acos(v); -} - double -asqrt(double v) { return ((v <= 0) ? 0. : sqrt(v)); } - double -aatan2(double n, double d) { - return ((fabs(n) < ATOL && fabs(d) < ATOL) ? 0. : atan2(n,d)); -} diff --git a/src/aasincos.cpp b/src/aasincos.cpp new file mode 100644 index 00000000..4f9ea25f --- /dev/null +++ b/src/aasincos.cpp @@ -0,0 +1,38 @@ +/* arc sin, cosine, tan2 and sqrt that will NOT fail */ + +#include + +#include "projects.h" + +#define ONE_TOL 1.00000000000001 +#define ATOL 1e-50 + + double +aasin(projCtx ctx,double v) { + double av; + + if ((av = fabs(v)) >= 1.) { + if (av > ONE_TOL) + pj_ctx_set_errno( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE ); + return (v < 0. ? -M_HALFPI : M_HALFPI); + } + return asin(v); +} + + double +aacos(projCtx ctx, double v) { + double av; + + if ((av = fabs(v)) >= 1.) { + if (av > ONE_TOL) + pj_ctx_set_errno( ctx, PJD_ERR_ACOS_ASIN_ARG_TOO_LARGE ); + return (v < 0. ? M_PI : 0.); + } + return acos(v); +} + double +asqrt(double v) { return ((v <= 0) ? 0. : sqrt(v)); } + double +aatan2(double n, double d) { + return ((fabs(n) < ATOL && fabs(d) < ATOL) ? 0. : atan2(n,d)); +} diff --git a/src/adjlon.c b/src/adjlon.c deleted file mode 100644 index 784a90aa..00000000 --- a/src/adjlon.c +++ /dev/null @@ -1,20 +0,0 @@ -/* reduce argument to range +/- PI */ -#include -#include "projects.h" - -double adjlon (double lon) { - /* Let lon slightly overshoot, to avoid spurious sign switching at the date line */ - if (fabs (lon) < M_PI + 1e-12) - return lon; - - /* adjust to 0..2pi range */ - lon += M_PI; - - /* remove integral # of 'revolutions'*/ - lon -= M_TWOPI * floor(lon / M_TWOPI); - - /* adjust back to -pi..pi range */ - lon -= M_PI; - - return lon; -} diff --git a/src/adjlon.cpp b/src/adjlon.cpp new file mode 100644 index 00000000..784a90aa --- /dev/null +++ b/src/adjlon.cpp @@ -0,0 +1,20 @@ +/* reduce argument to range +/- PI */ +#include +#include "projects.h" + +double adjlon (double lon) { + /* Let lon slightly overshoot, to avoid spurious sign switching at the date line */ + if (fabs (lon) < M_PI + 1e-12) + return lon; + + /* adjust to 0..2pi range */ + lon += M_PI; + + /* remove integral # of 'revolutions'*/ + lon -= M_TWOPI * floor(lon / M_TWOPI); + + /* adjust back to -pi..pi range */ + lon -= M_PI; + + return lon; +} diff --git a/src/bch2bps.c b/src/bch2bps.c deleted file mode 100644 index 3ee56993..00000000 --- a/src/bch2bps.c +++ /dev/null @@ -1,152 +0,0 @@ -/* convert bivariate w Chebyshev series to w Power series */ -#include "projects.h" -/* basic support procedures */ - static void /* clear vector to zero */ -clear(projUV *p, int n) { static const projUV c = {0., 0.}; while (n--) *p++ = c; } - static void /* clear matrix rows to zero */ -bclear(projUV **p, int n, int m) { while (n--) clear(*p++, m); } - static void /* move vector */ -bmove(projUV *a, projUV *b, int n) { while (n--) *a++ = *b++; } - static void /* a <- m * b - c */ -submop(projUV *a, double m, projUV *b, projUV *c, int n) { - while (n--) { - a->u = m * b->u - c->u; - a++->v = m * b++->v - c++->v; - } -} - static void /* a <- b - c */ -subop(projUV *a, projUV *b, projUV *c, int n) { - while (n--) { - a->u = b->u - c->u; - a++->v = b++->v - c++->v; - } -} - static void /* multiply vector a by scalar m */ -dmult(projUV *a, double m, int n) { while(n--) { a->u *= m; a->v *= m; ++a; } } - static void /* row adjust a[] <- a[] - m * b[] */ -dadd(projUV *a, projUV *b, double m, int n) { - while(n--) { - a->u -= m * b->u; - a++->v -= m * b++->v; - } -} - static int /* convert row to power series */ -rows(projUV *c, projUV *d, int n) { - projUV sv, *dd; - int j, k; - - dd = (projUV *)vector1(n-1, sizeof(projUV)); - if (!dd) - return 0; - sv.u = sv.v = 0.; - for (j = 0; j < n; ++j) d[j] = dd[j] = sv; - d[0] = c[n-1]; - for (j = n-2; j >= 1; --j) { - for (k = n-j; k >= 1; --k) { - sv = d[k]; - d[k].u = 2. * d[k-1].u - dd[k].u; - d[k].v = 2. * d[k-1].v - dd[k].v; - dd[k] = sv; - } - sv = d[0]; - d[0].u = -dd[0].u + c[j].u; - d[0].v = -dd[0].v + c[j].v; - dd[0] = sv; - } - for (j = n-1; j >= 1; --j) { - d[j].u = d[j-1].u - dd[j].u; - d[j].v = d[j-1].v - dd[j].v; - } - d[0].u = -dd[0].u + .5 * c[0].u; - d[0].v = -dd[0].v + .5 * c[0].v; - pj_dalloc(dd); - return 1; -} - static int /* convert columns to power series */ -cols(projUV **c, projUV **d, int nu, int nv) { - projUV *sv, **dd; - int j, k; - - dd = (projUV **)vector2(nu, nv, sizeof(projUV)); - if (!dd) - return 0; - sv = (projUV *)vector1(nv, sizeof(projUV)); - if (!sv) { - freev2((void **)dd, nu); - return 0; - } - bclear(d, nu, nv); - bclear(dd, nu, nv); - bmove(d[0], c[nu-1], nv); - for (j = nu-2; j >= 1; --j) { - for (k = nu-j; k >= 1; --k) { - bmove(sv, d[k], nv); - submop(d[k], 2., d[k-1], dd[k], nv); - bmove(dd[k], sv, nv); - } - bmove(sv, d[0], nv); - subop(d[0], c[j], dd[0], nv); - bmove(dd[0], sv, nv); - } - for (j = nu-1; j >= 1; --j) - subop(d[j], d[j-1], dd[j], nv); - submop(d[0], .5, c[0], dd[0], nv); - freev2((void **) dd, nu); - pj_dalloc(sv); - return 1; -} - static void /* row adjust for range -1 to 1 to a to b */ -rowshft(double a, double b, projUV *d, int n) { - int k, j; - double fac, cnst; - - cnst = 2. / (b - a); - fac = cnst; - for (j = 1; j < n; ++j) { - d[j].u *= fac; - d[j].v *= fac; - fac *= cnst; - } - cnst = .5 * (a + b); - for (j = 0; j <= n-2; ++j) - for (k = n - 2; k >= j; --k) { - d[k].u -= cnst * d[k+1].u; - d[k].v -= cnst * d[k+1].v; - } -} - static void /* column adjust for range -1 to 1 to a to b */ -colshft(double a, double b, projUV **d, int n, int m) { - int k, j; - double fac, cnst; - - cnst = 2. / (b - a); - fac = cnst; - for (j = 1; j < n; ++j) { - dmult(d[j], fac, m); - fac *= cnst; - } - cnst = .5 * (a + b); - for (j = 0; j <= n-2; ++j) - for (k = n - 2; k >= j; --k) - dadd(d[k], d[k+1], cnst, m); -} - int /* entry point */ -bch2bps(projUV a, projUV b, projUV **c, int nu, int nv) { - projUV **d; - int i; - - if (nu < 1 || nv < 1 || !(d = (projUV **)vector2(nu, nv, sizeof(projUV)))) - return 0; - /* do rows to power series */ - for (i = 0; i < nu; ++i) { - if (!rows(c[i], d[i], nv)) - return 0; - rowshft(a.v, b.v, d[i], nv); - } - /* do columns to power series */ - if (!cols(d, c, nu, nv)) - return 0; - colshft(a.u, b.u, c, nu, nv); - freev2((void **) d, nu); - return 1; -} diff --git a/src/bch2bps.cpp b/src/bch2bps.cpp new file mode 100644 index 00000000..3ee56993 --- /dev/null +++ b/src/bch2bps.cpp @@ -0,0 +1,152 @@ +/* convert bivariate w Chebyshev series to w Power series */ +#include "projects.h" +/* basic support procedures */ + static void /* clear vector to zero */ +clear(projUV *p, int n) { static const projUV c = {0., 0.}; while (n--) *p++ = c; } + static void /* clear matrix rows to zero */ +bclear(projUV **p, int n, int m) { while (n--) clear(*p++, m); } + static void /* move vector */ +bmove(projUV *a, projUV *b, int n) { while (n--) *a++ = *b++; } + static void /* a <- m * b - c */ +submop(projUV *a, double m, projUV *b, projUV *c, int n) { + while (n--) { + a->u = m * b->u - c->u; + a++->v = m * b++->v - c++->v; + } +} + static void /* a <- b - c */ +subop(projUV *a, projUV *b, projUV *c, int n) { + while (n--) { + a->u = b->u - c->u; + a++->v = b++->v - c++->v; + } +} + static void /* multiply vector a by scalar m */ +dmult(projUV *a, double m, int n) { while(n--) { a->u *= m; a->v *= m; ++a; } } + static void /* row adjust a[] <- a[] - m * b[] */ +dadd(projUV *a, projUV *b, double m, int n) { + while(n--) { + a->u -= m * b->u; + a++->v -= m * b++->v; + } +} + static int /* convert row to power series */ +rows(projUV *c, projUV *d, int n) { + projUV sv, *dd; + int j, k; + + dd = (projUV *)vector1(n-1, sizeof(projUV)); + if (!dd) + return 0; + sv.u = sv.v = 0.; + for (j = 0; j < n; ++j) d[j] = dd[j] = sv; + d[0] = c[n-1]; + for (j = n-2; j >= 1; --j) { + for (k = n-j; k >= 1; --k) { + sv = d[k]; + d[k].u = 2. * d[k-1].u - dd[k].u; + d[k].v = 2. * d[k-1].v - dd[k].v; + dd[k] = sv; + } + sv = d[0]; + d[0].u = -dd[0].u + c[j].u; + d[0].v = -dd[0].v + c[j].v; + dd[0] = sv; + } + for (j = n-1; j >= 1; --j) { + d[j].u = d[j-1].u - dd[j].u; + d[j].v = d[j-1].v - dd[j].v; + } + d[0].u = -dd[0].u + .5 * c[0].u; + d[0].v = -dd[0].v + .5 * c[0].v; + pj_dalloc(dd); + return 1; +} + static int /* convert columns to power series */ +cols(projUV **c, projUV **d, int nu, int nv) { + projUV *sv, **dd; + int j, k; + + dd = (projUV **)vector2(nu, nv, sizeof(projUV)); + if (!dd) + return 0; + sv = (projUV *)vector1(nv, sizeof(projUV)); + if (!sv) { + freev2((void **)dd, nu); + return 0; + } + bclear(d, nu, nv); + bclear(dd, nu, nv); + bmove(d[0], c[nu-1], nv); + for (j = nu-2; j >= 1; --j) { + for (k = nu-j; k >= 1; --k) { + bmove(sv, d[k], nv); + submop(d[k], 2., d[k-1], dd[k], nv); + bmove(dd[k], sv, nv); + } + bmove(sv, d[0], nv); + subop(d[0], c[j], dd[0], nv); + bmove(dd[0], sv, nv); + } + for (j = nu-1; j >= 1; --j) + subop(d[j], d[j-1], dd[j], nv); + submop(d[0], .5, c[0], dd[0], nv); + freev2((void **) dd, nu); + pj_dalloc(sv); + return 1; +} + static void /* row adjust for range -1 to 1 to a to b */ +rowshft(double a, double b, projUV *d, int n) { + int k, j; + double fac, cnst; + + cnst = 2. / (b - a); + fac = cnst; + for (j = 1; j < n; ++j) { + d[j].u *= fac; + d[j].v *= fac; + fac *= cnst; + } + cnst = .5 * (a + b); + for (j = 0; j <= n-2; ++j) + for (k = n - 2; k >= j; --k) { + d[k].u -= cnst * d[k+1].u; + d[k].v -= cnst * d[k+1].v; + } +} + static void /* column adjust for range -1 to 1 to a to b */ +colshft(double a, double b, projUV **d, int n, int m) { + int k, j; + double fac, cnst; + + cnst = 2. / (b - a); + fac = cnst; + for (j = 1; j < n; ++j) { + dmult(d[j], fac, m); + fac *= cnst; + } + cnst = .5 * (a + b); + for (j = 0; j <= n-2; ++j) + for (k = n - 2; k >= j; --k) + dadd(d[k], d[k+1], cnst, m); +} + int /* entry point */ +bch2bps(projUV a, projUV b, projUV **c, int nu, int nv) { + projUV **d; + int i; + + if (nu < 1 || nv < 1 || !(d = (projUV **)vector2(nu, nv, sizeof(projUV)))) + return 0; + /* do rows to power series */ + for (i = 0; i < nu; ++i) { + if (!rows(c[i], d[i], nv)) + return 0; + rowshft(a.v, b.v, d[i], nv); + } + /* do columns to power series */ + if (!cols(d, c, nu, nv)) + return 0; + colshft(a.u, b.u, c, nu, nv); + freev2((void **) d, nu); + return 1; +} diff --git a/src/bchgen.c b/src/bchgen.c deleted file mode 100644 index c129f0c1..00000000 --- a/src/bchgen.c +++ /dev/null @@ -1,58 +0,0 @@ -/* generate double bivariate Chebychev polynomial */ -#include "projects.h" - int -bchgen(projUV a, projUV b, int nu, int nv, projUV **f, projUV(*func)(projUV)) { - int i, j, k; - projUV arg, *t, bma, bpa, *c; - double d, fac; - - bma.u = 0.5 * (b.u - a.u); bma.v = 0.5 * (b.v - a.v); - bpa.u = 0.5 * (b.u + a.u); bpa.v = 0.5 * (b.v + a.v); - for ( i = 0; i < nu; ++i) { - arg.u = cos(M_PI * (i + 0.5) / nu) * bma.u + bpa.u; - for ( j = 0; j < nv; ++j) { - arg.v = cos(M_PI * (j + 0.5) / nv) * bma.v + bpa.v; - f[i][j] = (*func)(arg); - if ((f[i][j]).u == HUGE_VAL) - return(1); - } - } - if (!(c = (projUV *) vector1(nu, sizeof(projUV)))) return 1; - fac = 2. / nu; - for ( j = 0; j < nv ; ++j) { - for ( i = 0; i < nu; ++i) { - arg.u = arg.v = 0.; - for (k = 0; k < nu; ++k) { - d = cos(M_PI * i * (k + .5) / nu); - arg.u += f[k][j].u * d; - arg.v += f[k][j].v * d; - } - arg.u *= fac; - arg.v *= fac; - c[i] = arg; - } - for (i = 0; i < nu; ++i) - f[i][j] = c[i]; - } - pj_dalloc(c); - if (!(c = (projUV*) vector1(nv, sizeof(projUV)))) return 1; - fac = 2. / nv; - for ( i = 0; i < nu; ++i) { - t = f[i]; - for (j = 0; j < nv; ++j) { - arg.u = arg.v = 0.; - for (k = 0; k < nv; ++k) { - d = cos(M_PI * j * (k + .5) / nv); - arg.u += t[k].u * d; - arg.v += t[k].v * d; - } - arg.u *= fac; - arg.v *= fac; - c[j] = arg; - } - f[i] = c; - c = t; - } - pj_dalloc(c); - return(0); -} diff --git a/src/bchgen.cpp b/src/bchgen.cpp new file mode 100644 index 00000000..c129f0c1 --- /dev/null +++ b/src/bchgen.cpp @@ -0,0 +1,58 @@ +/* generate double bivariate Chebychev polynomial */ +#include "projects.h" + int +bchgen(projUV a, projUV b, int nu, int nv, projUV **f, projUV(*func)(projUV)) { + int i, j, k; + projUV arg, *t, bma, bpa, *c; + double d, fac; + + bma.u = 0.5 * (b.u - a.u); bma.v = 0.5 * (b.v - a.v); + bpa.u = 0.5 * (b.u + a.u); bpa.v = 0.5 * (b.v + a.v); + for ( i = 0; i < nu; ++i) { + arg.u = cos(M_PI * (i + 0.5) / nu) * bma.u + bpa.u; + for ( j = 0; j < nv; ++j) { + arg.v = cos(M_PI * (j + 0.5) / nv) * bma.v + bpa.v; + f[i][j] = (*func)(arg); + if ((f[i][j]).u == HUGE_VAL) + return(1); + } + } + if (!(c = (projUV *) vector1(nu, sizeof(projUV)))) return 1; + fac = 2. / nu; + for ( j = 0; j < nv ; ++j) { + for ( i = 0; i < nu; ++i) { + arg.u = arg.v = 0.; + for (k = 0; k < nu; ++k) { + d = cos(M_PI * i * (k + .5) / nu); + arg.u += f[k][j].u * d; + arg.v += f[k][j].v * d; + } + arg.u *= fac; + arg.v *= fac; + c[i] = arg; + } + for (i = 0; i < nu; ++i) + f[i][j] = c[i]; + } + pj_dalloc(c); + if (!(c = (projUV*) vector1(nv, sizeof(projUV)))) return 1; + fac = 2. / nv; + for ( i = 0; i < nu; ++i) { + t = f[i]; + for (j = 0; j < nv; ++j) { + arg.u = arg.v = 0.; + for (k = 0; k < nv; ++k) { + d = cos(M_PI * j * (k + .5) / nv); + arg.u += t[k].u * d; + arg.v += t[k].v * d; + } + arg.u *= fac; + arg.v *= fac; + c[j] = arg; + } + f[i] = c; + c = t; + } + pj_dalloc(c); + return(0); +} diff --git a/src/bin_cct.cmake b/src/bin_cct.cmake index 37752217..11643bca 100644 --- a/src/bin_cct.cmake +++ b/src/bin_cct.cmake @@ -1,4 +1,4 @@ -set(CCT_SRC cct.c proj_strtod.c proj_strtod.h) +set(CCT_SRC cct.cpp proj_strtod.cpp proj_strtod.h) set(CCT_INCLUDE optargpm.h) source_group("Source Files\\Bin" FILES ${CCT_SRC}) diff --git a/src/bin_cs2cs.cmake b/src/bin_cs2cs.cmake index 9c013dcc..70d3b04d 100644 --- a/src/bin_cs2cs.cmake +++ b/src/bin_cs2cs.cmake @@ -1,6 +1,6 @@ set(CS2CS_SRC cs2cs.cpp - gen_cheb.c - p_series.c) + gen_cheb.cpp + p_series.cpp) source_group("Source Files\\Bin" FILES ${CS2CS_SRC}) diff --git a/src/bin_geod.cmake b/src/bin_geod.cmake index 1f9ee788..fe0e5c08 100644 --- a/src/bin_geod.cmake +++ b/src/bin_geod.cmake @@ -1,5 +1,5 @@ -set(GEOD_SRC geod.c - geod_set.c geod_interface.c ) +set(GEOD_SRC geod.cpp + geod_set.cpp geod_interface.cpp ) set(GEOD_INCLUDE geod_interface.h) source_group("Source Files\\Bin" FILES ${GEOD_SRC} ${GEOD_INCLUDE}) diff --git a/src/bin_geodtest.cmake b/src/bin_geodtest.cmake index 743c630e..bfd6cf99 100644 --- a/src/bin_geodtest.cmake +++ b/src/bin_geodtest.cmake @@ -1,4 +1,4 @@ -set(GEODTEST_SRC geodtest.c ) +set(GEODTEST_SRC geodtest.cpp ) set(GEODTEST_INCLUDE) source_group("Source Files\\Bin" FILES ${GEODTEST_SRC} ${GEODTEST_INCLUDE}) diff --git a/src/bin_gie.cmake b/src/bin_gie.cmake index 596592fa..f59319fc 100644 --- a/src/bin_gie.cmake +++ b/src/bin_gie.cmake @@ -1,4 +1,4 @@ -set(GIE_SRC gie.c proj_strtod.c proj_strtod.h) +set(GIE_SRC gie.cpp proj_strtod.cpp proj_strtod.h) set(GIE_INCLUDE optargpm.h) source_group("Source Files\\Bin" FILES ${GIE_SRC}) diff --git a/src/bin_nad2bin.cmake b/src/bin_nad2bin.cmake index 0d0fd473..3e1fa422 100644 --- a/src/bin_nad2bin.cmake +++ b/src/bin_nad2bin.cmake @@ -3,7 +3,7 @@ if(WIN32 AND BUILD_LIBPROJ_SHARED) endif(WIN32 AND BUILD_LIBPROJ_SHARED) -set(NAD2BIN_SRC nad2bin.c) +set(NAD2BIN_SRC nad2bin.cpp) source_group("Source Files\\Bin" FILES ${NAD2BIN_SRC}) #Executable diff --git a/src/bin_proj.cmake b/src/bin_proj.cmake index fb7c885d..5911aa50 100644 --- a/src/bin_proj.cmake +++ b/src/bin_proj.cmake @@ -1,6 +1,6 @@ -set(PROJ_SRC proj.c - gen_cheb.c - p_series.c) +set(PROJ_SRC proj.cpp + gen_cheb.cpp + p_series.cpp) source_group("Source Files\\Bin" FILES ${PROJ_SRC}) diff --git a/src/biveval.c b/src/biveval.c deleted file mode 100644 index 05e83e21..00000000 --- a/src/biveval.c +++ /dev/null @@ -1,87 +0,0 @@ -/* procedures for evaluating Tseries */ -# include "projects.h" -# define NEAR_ONE 1.00001 -static double ceval(struct PW_COEF *C, int n, projUV w, projUV w2) { - double d=0, dd=0, vd, vdd, tmp, *c; - int j; - - for (C += n ; n-- ; --C ) { - if ( (j = C->m) != 0) { - vd = vdd = 0.; - for (c = C->c + --j; j ; --j ) { - vd = w2.v * (tmp = vd) - vdd + *c--; - vdd = tmp; - } - d = w2.u * (tmp = d) - dd + w.v * vd - vdd + 0.5 * *c; - } else - d = w2.u * (tmp = d) - dd; - dd = tmp; - } - if ( (j = C->m) != 0 ) { - vd = vdd = 0.; - for (c = C->c + --j; j ; --j ) { - vd = w2.v * (tmp = vd) - vdd + *c--; - vdd = tmp; - } - return (w.u * d - dd + 0.5 * ( w.v * vd - vdd + 0.5 * *c )); - } else - return (w.u * d - dd); -} - -projUV /* bivariate Chebyshev polynomial entry point */ -bcheval(projUV in, Tseries *T) { - projUV w2, w; - projUV out; - /* scale to +-1 */ - w.u = ( in.u + in.u - T->a.u ) * T->b.u; - w.v = ( in.v + in.v - T->a.v ) * T->b.v; - if (fabs(w.u) > NEAR_ONE || fabs(w.v) > NEAR_ONE) { - out.u = out.v = HUGE_VAL; - pj_errno = PJD_ERR_TCHEBY_VAL_OUT_OF_RANGE; - } else { /* double evaluation */ - w2.u = w.u + w.u; - w2.v = w.v + w.v; - out.u = ceval(T->cu, T->mu, w, w2); - out.v = ceval(T->cv, T->mv, w, w2); - } - return out; -} - -projUV /* bivariate power polynomial entry point */ -bpseval(projUV in, Tseries *T) { - projUV out; - double *c, row; - int i, m; - - out.u = out.v = 0.; - for (i = T->mu; i >= 0; --i) { - row = 0.; - if ((m = T->cu[i].m) != 0) { - c = T->cu[i].c + m; - while (m--) - row = *--c + in.v * row; - } - out.u = row + in.u * out.u; - } - for (i = T->mv; i >= 0; --i) { - row = 0.; - if ((m = T->cv[i].m) != 0) { - c = T->cv[i].c + m; - while (m--) - row = *--c + in.v * row; - } - out.v = row + in.u * out.v; - } - return out; -} - -projUV /* general entry point selecting evaluation mode */ -biveval(projUV in, Tseries *T) { - - if (T->power) { - return bpseval(in, T); - } else { - return bcheval(in, T); - } -} - diff --git a/src/biveval.cpp b/src/biveval.cpp new file mode 100644 index 00000000..05e83e21 --- /dev/null +++ b/src/biveval.cpp @@ -0,0 +1,87 @@ +/* procedures for evaluating Tseries */ +# include "projects.h" +# define NEAR_ONE 1.00001 +static double ceval(struct PW_COEF *C, int n, projUV w, projUV w2) { + double d=0, dd=0, vd, vdd, tmp, *c; + int j; + + for (C += n ; n-- ; --C ) { + if ( (j = C->m) != 0) { + vd = vdd = 0.; + for (c = C->c + --j; j ; --j ) { + vd = w2.v * (tmp = vd) - vdd + *c--; + vdd = tmp; + } + d = w2.u * (tmp = d) - dd + w.v * vd - vdd + 0.5 * *c; + } else + d = w2.u * (tmp = d) - dd; + dd = tmp; + } + if ( (j = C->m) != 0 ) { + vd = vdd = 0.; + for (c = C->c + --j; j ; --j ) { + vd = w2.v * (tmp = vd) - vdd + *c--; + vdd = tmp; + } + return (w.u * d - dd + 0.5 * ( w.v * vd - vdd + 0.5 * *c )); + } else + return (w.u * d - dd); +} + +projUV /* bivariate Chebyshev polynomial entry point */ +bcheval(projUV in, Tseries *T) { + projUV w2, w; + projUV out; + /* scale to +-1 */ + w.u = ( in.u + in.u - T->a.u ) * T->b.u; + w.v = ( in.v + in.v - T->a.v ) * T->b.v; + if (fabs(w.u) > NEAR_ONE || fabs(w.v) > NEAR_ONE) { + out.u = out.v = HUGE_VAL; + pj_errno = PJD_ERR_TCHEBY_VAL_OUT_OF_RANGE; + } else { /* double evaluation */ + w2.u = w.u + w.u; + w2.v = w.v + w.v; + out.u = ceval(T->cu, T->mu, w, w2); + out.v = ceval(T->cv, T->mv, w, w2); + } + return out; +} + +projUV /* bivariate power polynomial entry point */ +bpseval(projUV in, Tseries *T) { + projUV out; + double *c, row; + int i, m; + + out.u = out.v = 0.; + for (i = T->mu; i >= 0; --i) { + row = 0.; + if ((m = T->cu[i].m) != 0) { + c = T->cu[i].c + m; + while (m--) + row = *--c + in.v * row; + } + out.u = row + in.u * out.u; + } + for (i = T->mv; i >= 0; --i) { + row = 0.; + if ((m = T->cv[i].m) != 0) { + c = T->cv[i].c + m; + while (m--) + row = *--c + in.v * row; + } + out.v = row + in.u * out.v; + } + return out; +} + +projUV /* general entry point selecting evaluation mode */ +biveval(projUV in, Tseries *T) { + + if (T->power) { + return bpseval(in, T); + } else { + return bcheval(in, T); + } +} + diff --git a/src/cct.c b/src/cct.c deleted file mode 100644 index 54734b98..00000000 --- a/src/cct.c +++ /dev/null @@ -1,472 +0,0 @@ -/*********************************************************************** - - The cct 4D Transformation program - -************************************************************************ - -cct is a 4D equivalent to the "proj" projection program. - -cct is an acronym meaning "Coordinate Conversion and Transformation". - -The acronym refers to definitions given in the OGC 08-015r2/ISO-19111 -standard "Geographical Information -- Spatial Referencing by Coordinates", -which defines two different classes of coordinate operations: - -*Coordinate Conversions*, which are coordinate operations where input -and output datum are identical (e.g. conversion from geographical to -cartesian coordinates) and - -*Coordinate Transformations*, which are coordinate operations where -input and output datums differ (e.g. change of reference frame). - -cct, however, also refers to Carl Christian Tscherning (1942--2014), -professor of Geodesy at the University of Copenhagen, mentor and advisor -for a generation of Danish geodesists, colleague and collaborator for -two generations of global geodesists, Secretary General for the -International Association of Geodesy, IAG (1995--2007), fellow of the -American Geophysical Union (1991), recipient of the IAG Levallois Medal -(2007), the European Geosciences Union Vening Meinesz Medal (2008), and -of numerous other honours. - -cct, or Christian, as he was known to most of us, was recognized for his -good mood, his sharp wit, his tireless work, and his great commitment to -the development of geodesy - both through his scientific contributions, -comprising more than 250 publications, and by his mentoring and teaching -of the next generations of geodesists. - -As Christian was an avid Fortran programmer, and a keen Unix connoisseur, -he would have enjoyed to know that his initials would be used to name a -modest Unix style transformation filter, hinting at the tireless aspect -of his personality, which was certainly one of the reasons he accomplished -so much, and meant so much to so many people. - -Hence, in honour of cct (the geodesist) this is cct (the program). - -************************************************************************ - -Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 - -************************************************************************ - -* Copyright (c) 2016, 2017 Thomas Knudsen -* Copyright (c) 2017, SDFE -* -* 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 -#include -#include -#include -#include -#include - -#include "proj.h" -#include "proj_internal.h" -#include "proj_strtod.h" -#include "projects.h" -#include "optargpm.h" - - -static void logger(void *data, int level, const char *msg); -static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...); - -/* Prototypes from functions in this file */ -char *column (char *buf, int n); -PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time); - - -static const char usage[] = { - "--------------------------------------------------------------------------------\n" - "Usage: %s [-options]... [+operator_specs]... infile...\n" - "--------------------------------------------------------------------------------\n" - "Options:\n" - "--------------------------------------------------------------------------------\n" - " -c x,y,z,t Specify input columns for (up to) 4 input parameters.\n" - " Defaults to 1,2,3,4\n" - " -d n Specify number of decimals in output.\n" - " -I Do the inverse transformation\n" - " -o /path/to/file Specify output file name\n" - " -t value Provide a fixed t value for all input data (e.g. -t 0)\n" - " -z value Provide a fixed z value for all input data (e.g. -z 0)\n" - " -s n Skip n first lines of a infile\n" - " -v Verbose: Provide non-essential informational output.\n" - " Repeat -v for more verbosity (e.g. -vv)\n" - "--------------------------------------------------------------------------------\n" - "Long Options:\n" - "--------------------------------------------------------------------------------\n" - " --output Alias for -o\n" - " --columns Alias for -c\n" - " --decimals Alias for -d\n" - " --height Alias for -z\n" - " --time Alias for -t\n" - " --verbose Alias for -v\n" - " --inverse Alias for -I\n" - " --skip-lines Alias for -s\n" - " --help Alias for -h\n" - " --version Print version number\n" - "--------------------------------------------------------------------------------\n" - "Operator Specs:\n" - "--------------------------------------------------------------------------------\n" - "The operator specs describe the action to be performed by cct, e.g:\n" - "\n" - " +proj=utm +ellps=GRS80 +zone=32\n" - "\n" - "instructs cct to convert input data to Universal Transverse Mercator, zone 32\n" - "coordinates, based on the GRS80 ellipsoid.\n" - "\n" - "Hence, the command\n" - "\n" - " echo 12 55 | cct -z0 -t0 +proj=utm +zone=32 +ellps=GRS80\n" - "\n" - "Should give results comparable to the classic proj command\n" - "\n" - " echo 12 55 | proj +proj=utm +zone=32 +ellps=GRS80\n" - "--------------------------------------------------------------------------------\n" - "Examples:\n" - "--------------------------------------------------------------------------------\n" - "1. convert geographical input to UTM zone 32 on the GRS80 ellipsoid:\n" - " cct +proj=utm +ellps=GRS80 +zone=32\n" - "2. roundtrip accuracy check for the case above:\n" - " cct +proj=pipeline +proj=utm +ellps=GRS80 +zone=32 +step +step +inv\n" - "3. as (1) but specify input columns for longitude, latitude, height and time:\n" - " cct -c 5,2,1,4 +proj=utm +ellps=GRS80 +zone=32\n" - "4. as (1) but specify fixed height and time, hence needing only 2 cols in input:\n" - " cct -t 0 -z 0 +proj=utm +ellps=GRS80 +zone=32\n" - "--------------------------------------------------------------------------------\n" -}; - - -static void logger(void *data, int level, const char *msg) { - FILE *stream; - int log_tell = proj_log_level(PJ_DEFAULT_CTX, PJ_LOG_TELL); - - stream = (FILE *) data; - - /* if we use PJ_LOG_NONE we always want to print stuff to stream */ - if (level == PJ_LOG_NONE) { - fprintf(stream, "%s", msg); - return; - } - - /* should always print to stderr if level == PJ_LOG_ERROR */ - if (level == PJ_LOG_ERROR) { - fprintf(stderr, "%s", msg); - return; - } - - /* otherwise only print if log level set by user is high enough */ - if (level <= log_tell) - fprintf(stream, "%s", msg); -} - -FILE *fout; - -static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...) { - - va_list args; - char *msg_buf; - - va_start( args, fmt ); - - msg_buf = (char *) malloc(100000); - if( msg_buf == NULL ) { - va_end( args ); - return; - } - - vsprintf( msg_buf, fmt, args ); - - logger((void *) fout, log_level, msg_buf); - - va_end( args ); - free( msg_buf ); -} - - -int main(int argc, char **argv) { - PJ *P; - PJ_COORD point; - PJ_PROJ_INFO info; - OPTARGS *o; - char blank_comment[] = ""; - char whitespace[] = " "; - char *comment; - char *comment_delimiter; - char *buf; - int i, nfields = 4, direction = 1, skip_lines = 0, verbose; - double fixed_z = HUGE_VAL, fixed_time = HUGE_VAL; - int decimals_angles = 10; - int decimals_distances = 4; - int columns_xyzt[] = {1, 2, 3, 4}; - const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", 0}; - const char *longkeys[] = { - "o=output", - "c=columns", - "d=decimals", - "z=height", - "t=time", - "s=skip-lines", - 0}; - - fout = stdout; - - o = opt_parse (argc, argv, "hvI", "cdozts", longflags, longkeys); - if (0==o) - return 0; - - if (opt_given (o, "h") || argc==1) { - printf (usage, o->progname); - return 0; - } - - direction = opt_given (o, "I")? -1: 1; - - verbose = MIN(opt_given (o, "v"), 3); /* log level can't be larger than 3 */ - proj_log_level (PJ_DEFAULT_CTX, verbose); - proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); - - if (opt_given (o, "version")) { - print (PJ_LOG_NONE, "%s: %s\n", o->progname, pj_get_release ()); - return 0; - } - - if (opt_given (o, "o")) - fout = fopen (opt_arg (o, "output"), "wt"); - if (0==fout) { - print (PJ_LOG_ERROR, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); - free (o); - return 1; - } - - print (PJ_LOG_TRACE, "%s: Running in very verbose mode\n", o->progname); - - if (opt_given (o, "z")) { - fixed_z = proj_atof (opt_arg (o, "z")); - nfields--; - } - - if (opt_given (o, "t")) { - fixed_time = proj_atof (opt_arg (o, "t")); - nfields--; - } - - if (opt_given (o, "d")) { - int dec = atoi (opt_arg (o, "d")); - decimals_angles = dec; - decimals_distances = dec; - } - - if (opt_given (o, "s")) { - skip_lines = atoi (opt_arg(o, "s")); - } - - if (opt_given (o, "c")) { - int ncols; - /* reset column numbers to ease comment output later on */ - for (i=0; i<4; i++) - columns_xyzt[i] = 0; - - /* cppcheck-suppress invalidscanf */ - ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+2, columns_xyzt+3); - if (ncols != nfields) { - print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'\n", o->progname, opt_arg (o, "c")); - free (o); - if (stdout != fout) - fclose (fout); - return 1; - } - } - - /* Setup transformation */ - P = proj_create_argv (0, o->pargc, o->pargv); - if ((0==P) || (0==o->pargc)) { - print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help\n", - o->progname, pj_strerrno (proj_errno(P)), o->progname); - free (o); - if (stdout != fout) - fclose (fout); - return 1; - } - - info = proj_pj_info (P); - print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d\n", info.definition, argc, o->pargc); - - if (direction==-1) { - /* fail if an inverse operation is not available */ - if (!info.has_inverse) { - print (PJ_LOG_ERROR, "Inverse operation not available\n"); - if (stdout != fout) - fclose (fout); - return 1; - } - /* We have no API call for inverting an operation, so we brute force it. */ - P->inverted = !(P->inverted); - } - direction = 1; - - /* Allocate input buffer */ - buf = calloc (1, 10000); - if (0==buf) { - print (PJ_LOG_ERROR, "%s: Out of memory\n", o->progname); - pj_free (P); - free (o); - if (stdout != fout) - fclose (fout); - return 1; - } - - - /* Loop over all records of all input files */ - while (opt_input_loop (o, optargs_file_format_text)) { - int err; - void *ret = fgets (buf, 10000, o->input); - char *c = column (buf, 1); - opt_eof_handler (o); - if (0==ret) { - print (PJ_LOG_ERROR, "Read error in record %d\n", (int) o->record_index); - continue; - } - point = parse_input_line (buf, columns_xyzt, fixed_z, fixed_time); - if (skip_lines > 0) { - skip_lines--; - continue; - } - - /* if it's a comment or blank line, we reflect it */ - if (c && ((*c=='\0') || (*c=='#'))) { - fprintf (fout, "%s", buf); - continue; - } - - if (HUGE_VAL==point.xyzt.x) { - /* otherwise, it must be a syntax error */ - print (PJ_LOG_NONE, "# Record %d UNREADABLE: %s", (int) o->record_index, buf); - print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d\n", o->progname, opt_filename (o), opt_record (o)); - continue; - } - - if (proj_angular_input (P, direction)) { - point.lpzt.lam = proj_torad (point.lpzt.lam); - point.lpzt.phi = proj_torad (point.lpzt.phi); - } - err = proj_errno_reset (P); - point = proj_trans (P, direction, point); - - if (HUGE_VAL==point.xyzt.x) { - /* transformation error */ - print (PJ_LOG_NONE, "# Record %d TRANSFORMATION ERROR: %s (%s)", - (int) o->record_index, buf, pj_strerrno (proj_errno(P))); - proj_errno_restore (P, err); - continue; - } - proj_errno_restore (P, err); - - /* handle comment string */ - comment = column(buf, nfields+1); - if (opt_given(o, "c")) { - /* what number is the last coordinate column in the input data? */ - int colmax = 0; - for (i=0; i<4; i++) - colmax = MAX(colmax, columns_xyzt[i]); - comment = column(buf, colmax+1); - } - comment_delimiter = (comment && *comment) ? whitespace : blank_comment; - - /* Time to print the result */ - if (proj_angular_output (P, direction)) { - point.lpzt.lam = proj_todeg (point.lpzt.lam); - point.lpzt.phi = proj_todeg (point.lpzt.phi); - print (PJ_LOG_NONE, "%14.*f %14.*f %12.*f %12.4f%s%s\n", - decimals_angles, point.xyzt.x, - decimals_angles, point.xyzt.y, - decimals_distances, point.xyzt.z, - point.xyzt.t, comment_delimiter, comment - ); - } - else - print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s\n", - decimals_distances, point.xyzt.x, - decimals_distances, point.xyzt.y, - decimals_distances, point.xyzt.z, - point.xyzt.t, comment_delimiter, comment - ); - } - - if (stdout != fout) - fclose (fout); - free (o); - free (buf); - return 0; -} - - - - - -/* return a pointer to the n'th column of buf */ -char *column (char *buf, int n) { - int i; - if (n <= 0) - return buf; - for (i = 0; i < n; i++) { - while (isspace(*buf)) - buf++; - if (i == n - 1) - break; - while ((0 != *buf) && !isspace(*buf)) - buf++; - } - return buf; -} - -/* column to double */ -static double cold (char *args, int col) { - char *endp; - char *target; - double d; - target = column (args, col); - d = proj_strtod (target, &endp); - if (endp==target) - return HUGE_VAL; - return d; -} - -PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time) { - PJ_COORD err = proj_coord (HUGE_VAL, HUGE_VAL, HUGE_VAL, HUGE_VAL); - PJ_COORD result = err; - int prev_errno = errno; - errno = 0; - - result.xyzt.z = fixed_height; - result.xyzt.t = fixed_time; - result.xyzt.x = cold (buf, columns[0]); - result.xyzt.y = cold (buf, columns[1]); - if (result.xyzt.z==HUGE_VAL) - result.xyzt.z = cold (buf, columns[2]); - if (result.xyzt.t==HUGE_VAL) - result.xyzt.t = cold (buf, columns[3]); - - if (0!=errno) - return err; - - errno = prev_errno; - return result; -} diff --git a/src/cct.cpp b/src/cct.cpp new file mode 100644 index 00000000..0493e721 --- /dev/null +++ b/src/cct.cpp @@ -0,0 +1,472 @@ +/*********************************************************************** + + The cct 4D Transformation program + +************************************************************************ + +cct is a 4D equivalent to the "proj" projection program. + +cct is an acronym meaning "Coordinate Conversion and Transformation". + +The acronym refers to definitions given in the OGC 08-015r2/ISO-19111 +standard "Geographical Information -- Spatial Referencing by Coordinates", +which defines two different classes of coordinate operations: + +*Coordinate Conversions*, which are coordinate operations where input +and output datum are identical (e.g. conversion from geographical to +cartesian coordinates) and + +*Coordinate Transformations*, which are coordinate operations where +input and output datums differ (e.g. change of reference frame). + +cct, however, also refers to Carl Christian Tscherning (1942--2014), +professor of Geodesy at the University of Copenhagen, mentor and advisor +for a generation of Danish geodesists, colleague and collaborator for +two generations of global geodesists, Secretary General for the +International Association of Geodesy, IAG (1995--2007), fellow of the +American Geophysical Union (1991), recipient of the IAG Levallois Medal +(2007), the European Geosciences Union Vening Meinesz Medal (2008), and +of numerous other honours. + +cct, or Christian, as he was known to most of us, was recognized for his +good mood, his sharp wit, his tireless work, and his great commitment to +the development of geodesy - both through his scientific contributions, +comprising more than 250 publications, and by his mentoring and teaching +of the next generations of geodesists. + +As Christian was an avid Fortran programmer, and a keen Unix connoisseur, +he would have enjoyed to know that his initials would be used to name a +modest Unix style transformation filter, hinting at the tireless aspect +of his personality, which was certainly one of the reasons he accomplished +so much, and meant so much to so many people. + +Hence, in honour of cct (the geodesist) this is cct (the program). + +************************************************************************ + +Thomas Knudsen, thokn@sdfe.dk, 2016-05-25/2017-10-26 + +************************************************************************ + +* Copyright (c) 2016, 2017 Thomas Knudsen +* Copyright (c) 2017, SDFE +* +* 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 +#include +#include +#include +#include +#include + +#include "proj.h" +#include "proj_internal.h" +#include "proj_strtod.h" +#include "projects.h" +#include "optargpm.h" + + +static void logger(void *data, int level, const char *msg); +static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...); + +/* Prototypes from functions in this file */ +char *column (char *buf, int n); +PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time); + + +static const char usage[] = { + "--------------------------------------------------------------------------------\n" + "Usage: %s [-options]... [+operator_specs]... infile...\n" + "--------------------------------------------------------------------------------\n" + "Options:\n" + "--------------------------------------------------------------------------------\n" + " -c x,y,z,t Specify input columns for (up to) 4 input parameters.\n" + " Defaults to 1,2,3,4\n" + " -d n Specify number of decimals in output.\n" + " -I Do the inverse transformation\n" + " -o /path/to/file Specify output file name\n" + " -t value Provide a fixed t value for all input data (e.g. -t 0)\n" + " -z value Provide a fixed z value for all input data (e.g. -z 0)\n" + " -s n Skip n first lines of a infile\n" + " -v Verbose: Provide non-essential informational output.\n" + " Repeat -v for more verbosity (e.g. -vv)\n" + "--------------------------------------------------------------------------------\n" + "Long Options:\n" + "--------------------------------------------------------------------------------\n" + " --output Alias for -o\n" + " --columns Alias for -c\n" + " --decimals Alias for -d\n" + " --height Alias for -z\n" + " --time Alias for -t\n" + " --verbose Alias for -v\n" + " --inverse Alias for -I\n" + " --skip-lines Alias for -s\n" + " --help Alias for -h\n" + " --version Print version number\n" + "--------------------------------------------------------------------------------\n" + "Operator Specs:\n" + "--------------------------------------------------------------------------------\n" + "The operator specs describe the action to be performed by cct, e.g:\n" + "\n" + " +proj=utm +ellps=GRS80 +zone=32\n" + "\n" + "instructs cct to convert input data to Universal Transverse Mercator, zone 32\n" + "coordinates, based on the GRS80 ellipsoid.\n" + "\n" + "Hence, the command\n" + "\n" + " echo 12 55 | cct -z0 -t0 +proj=utm +zone=32 +ellps=GRS80\n" + "\n" + "Should give results comparable to the classic proj command\n" + "\n" + " echo 12 55 | proj +proj=utm +zone=32 +ellps=GRS80\n" + "--------------------------------------------------------------------------------\n" + "Examples:\n" + "--------------------------------------------------------------------------------\n" + "1. convert geographical input to UTM zone 32 on the GRS80 ellipsoid:\n" + " cct +proj=utm +ellps=GRS80 +zone=32\n" + "2. roundtrip accuracy check for the case above:\n" + " cct +proj=pipeline +proj=utm +ellps=GRS80 +zone=32 +step +step +inv\n" + "3. as (1) but specify input columns for longitude, latitude, height and time:\n" + " cct -c 5,2,1,4 +proj=utm +ellps=GRS80 +zone=32\n" + "4. as (1) but specify fixed height and time, hence needing only 2 cols in input:\n" + " cct -t 0 -z 0 +proj=utm +ellps=GRS80 +zone=32\n" + "--------------------------------------------------------------------------------\n" +}; + + +static void logger(void *data, int level, const char *msg) { + FILE *stream; + int log_tell = proj_log_level(PJ_DEFAULT_CTX, PJ_LOG_TELL); + + stream = (FILE *) data; + + /* if we use PJ_LOG_NONE we always want to print stuff to stream */ + if (level == PJ_LOG_NONE) { + fprintf(stream, "%s", msg); + return; + } + + /* should always print to stderr if level == PJ_LOG_ERROR */ + if (level == PJ_LOG_ERROR) { + fprintf(stderr, "%s", msg); + return; + } + + /* otherwise only print if log level set by user is high enough */ + if (level <= log_tell) + fprintf(stream, "%s", msg); +} + +FILE *fout; + +static void print(PJ_LOG_LEVEL log_level, const char *fmt, ...) { + + va_list args; + char *msg_buf; + + va_start( args, fmt ); + + msg_buf = (char *) malloc(100000); + if( msg_buf == NULL ) { + va_end( args ); + return; + } + + vsprintf( msg_buf, fmt, args ); + + logger((void *) fout, log_level, msg_buf); + + va_end( args ); + free( msg_buf ); +} + + +int main(int argc, char **argv) { + PJ *P; + PJ_COORD point; + PJ_PROJ_INFO info; + OPTARGS *o; + char blank_comment[] = ""; + char whitespace[] = " "; + char *comment; + char *comment_delimiter; + char *buf; + int i, nfields = 4, skip_lines = 0, verbose; + double fixed_z = HUGE_VAL, fixed_time = HUGE_VAL; + int decimals_angles = 10; + int decimals_distances = 4; + int columns_xyzt[] = {1, 2, 3, 4}; + const char *longflags[] = {"v=verbose", "h=help", "I=inverse", "version", 0}; + const char *longkeys[] = { + "o=output", + "c=columns", + "d=decimals", + "z=height", + "t=time", + "s=skip-lines", + 0}; + + fout = stdout; + + o = opt_parse (argc, argv, "hvI", "cdozts", longflags, longkeys); + if (0==o) + return 0; + + if (opt_given (o, "h") || argc==1) { + printf (usage, o->progname); + return 0; + } + + PJ_DIRECTION direction = opt_given (o, "I")? PJ_INV: PJ_FWD; + + verbose = MIN(opt_given (o, "v"), 3); /* log level can't be larger than 3 */ + proj_log_level (PJ_DEFAULT_CTX, static_cast(verbose)); + proj_log_func (PJ_DEFAULT_CTX, (void *) fout, logger); + + if (opt_given (o, "version")) { + print (PJ_LOG_NONE, "%s: %s\n", o->progname, pj_get_release ()); + return 0; + } + + if (opt_given (o, "o")) + fout = fopen (opt_arg (o, "output"), "wt"); + if (0==fout) { + print (PJ_LOG_ERROR, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); + free (o); + return 1; + } + + print (PJ_LOG_TRACE, "%s: Running in very verbose mode\n", o->progname); + + if (opt_given (o, "z")) { + fixed_z = proj_atof (opt_arg (o, "z")); + nfields--; + } + + if (opt_given (o, "t")) { + fixed_time = proj_atof (opt_arg (o, "t")); + nfields--; + } + + if (opt_given (o, "d")) { + int dec = atoi (opt_arg (o, "d")); + decimals_angles = dec; + decimals_distances = dec; + } + + if (opt_given (o, "s")) { + skip_lines = atoi (opt_arg(o, "s")); + } + + if (opt_given (o, "c")) { + int ncols; + /* reset column numbers to ease comment output later on */ + for (i=0; i<4; i++) + columns_xyzt[i] = 0; + + /* cppcheck-suppress invalidscanf */ + ncols = sscanf (opt_arg (o, "c"), "%d,%d,%d,%d", columns_xyzt, columns_xyzt+1, columns_xyzt+2, columns_xyzt+3); + if (ncols != nfields) { + print (PJ_LOG_ERROR, "%s: Too few input columns given: '%s'\n", o->progname, opt_arg (o, "c")); + free (o); + if (stdout != fout) + fclose (fout); + return 1; + } + } + + /* Setup transformation */ + P = proj_create_argv (0, o->pargc, o->pargv); + if ((0==P) || (0==o->pargc)) { + print (PJ_LOG_ERROR, "%s: Bad transformation arguments - (%s)\n '%s -h' for help\n", + o->progname, pj_strerrno (proj_errno(P)), o->progname); + free (o); + if (stdout != fout) + fclose (fout); + return 1; + } + + info = proj_pj_info (P); + print (PJ_LOG_TRACE, "Final: %s argc=%d pargc=%d\n", info.definition, argc, o->pargc); + + if (direction== PJ_INV) { + /* fail if an inverse operation is not available */ + if (!info.has_inverse) { + print (PJ_LOG_ERROR, "Inverse operation not available\n"); + if (stdout != fout) + fclose (fout); + return 1; + } + /* We have no API call for inverting an operation, so we brute force it. */ + P->inverted = !(P->inverted); + } + direction = PJ_FWD; + + /* Allocate input buffer */ + buf = static_cast(calloc (1, 10000)); + if (0==buf) { + print (PJ_LOG_ERROR, "%s: Out of memory\n", o->progname); + pj_free (P); + free (o); + if (stdout != fout) + fclose (fout); + return 1; + } + + + /* Loop over all records of all input files */ + while (opt_input_loop (o, optargs_file_format_text)) { + int err; + void *ret = fgets (buf, 10000, o->input); + char *c = column (buf, 1); + opt_eof_handler (o); + if (0==ret) { + print (PJ_LOG_ERROR, "Read error in record %d\n", (int) o->record_index); + continue; + } + point = parse_input_line (buf, columns_xyzt, fixed_z, fixed_time); + if (skip_lines > 0) { + skip_lines--; + continue; + } + + /* if it's a comment or blank line, we reflect it */ + if (c && ((*c=='\0') || (*c=='#'))) { + fprintf (fout, "%s", buf); + continue; + } + + if (HUGE_VAL==point.xyzt.x) { + /* otherwise, it must be a syntax error */ + print (PJ_LOG_NONE, "# Record %d UNREADABLE: %s", (int) o->record_index, buf); + print (PJ_LOG_ERROR, "%s: Could not parse file '%s' line %d\n", o->progname, opt_filename (o), opt_record (o)); + continue; + } + + if (proj_angular_input (P, direction)) { + point.lpzt.lam = proj_torad (point.lpzt.lam); + point.lpzt.phi = proj_torad (point.lpzt.phi); + } + err = proj_errno_reset (P); + point = proj_trans (P, direction, point); + + if (HUGE_VAL==point.xyzt.x) { + /* transformation error */ + print (PJ_LOG_NONE, "# Record %d TRANSFORMATION ERROR: %s (%s)", + (int) o->record_index, buf, pj_strerrno (proj_errno(P))); + proj_errno_restore (P, err); + continue; + } + proj_errno_restore (P, err); + + /* handle comment string */ + comment = column(buf, nfields+1); + if (opt_given(o, "c")) { + /* what number is the last coordinate column in the input data? */ + int colmax = 0; + for (i=0; i<4; i++) + colmax = MAX(colmax, columns_xyzt[i]); + comment = column(buf, colmax+1); + } + comment_delimiter = (comment && *comment) ? whitespace : blank_comment; + + /* Time to print the result */ + if (proj_angular_output (P, direction)) { + point.lpzt.lam = proj_todeg (point.lpzt.lam); + point.lpzt.phi = proj_todeg (point.lpzt.phi); + print (PJ_LOG_NONE, "%14.*f %14.*f %12.*f %12.4f%s%s\n", + decimals_angles, point.xyzt.x, + decimals_angles, point.xyzt.y, + decimals_distances, point.xyzt.z, + point.xyzt.t, comment_delimiter, comment + ); + } + else + print (PJ_LOG_NONE, "%13.*f %13.*f %12.*f %12.4f%s%s\n", + decimals_distances, point.xyzt.x, + decimals_distances, point.xyzt.y, + decimals_distances, point.xyzt.z, + point.xyzt.t, comment_delimiter, comment + ); + } + + if (stdout != fout) + fclose (fout); + free (o); + free (buf); + return 0; +} + + + + + +/* return a pointer to the n'th column of buf */ +char *column (char *buf, int n) { + int i; + if (n <= 0) + return buf; + for (i = 0; i < n; i++) { + while (isspace(*buf)) + buf++; + if (i == n - 1) + break; + while ((0 != *buf) && !isspace(*buf)) + buf++; + } + return buf; +} + +/* column to double */ +static double cold (char *args, int col) { + char *endp; + char *target; + double d; + target = column (args, col); + d = proj_strtod (target, &endp); + if (endp==target) + return HUGE_VAL; + return d; +} + +PJ_COORD parse_input_line (char *buf, int *columns, double fixed_height, double fixed_time) { + PJ_COORD err = proj_coord (HUGE_VAL, HUGE_VAL, HUGE_VAL, HUGE_VAL); + PJ_COORD result = err; + int prev_errno = errno; + errno = 0; + + result.xyzt.z = fixed_height; + result.xyzt.t = fixed_time; + result.xyzt.x = cold (buf, columns[0]); + result.xyzt.y = cold (buf, columns[1]); + if (result.xyzt.z==HUGE_VAL) + result.xyzt.z = cold (buf, columns[2]); + if (result.xyzt.t==HUGE_VAL) + result.xyzt.t = cold (buf, columns[3]); + + if (0!=errno) + return err; + + errno = prev_errno; + return result; +} diff --git a/src/dmstor.c b/src/dmstor.c deleted file mode 100644 index ab8e33f4..00000000 --- a/src/dmstor.c +++ /dev/null @@ -1,121 +0,0 @@ -/* Convert DMS string to radians */ - -#include -#include -#include -#include - -#include "projects.h" - -static double proj_strtod(char *nptr, char **endptr); - -/* following should be sufficient for all but the ridiculous */ -#define MAX_WORK 64 - static const char -*sym = "NnEeSsWw"; - static const double -vm[] = { - DEG_TO_RAD, - .0002908882086657216, - .0000048481368110953599 -}; - double -dmstor(const char *is, char **rs) { - return dmstor_ctx( pj_get_default_ctx(), is, rs ); -} - - double -dmstor_ctx(projCtx ctx, const char *is, char **rs) { - int sign, n, nl; - char *p, *s, work[MAX_WORK]; - double v, tv; - - if (rs) - *rs = (char *)is; - /* copy sting into work space */ - while (isspace(sign = *is)) ++is; - n = MAX_WORK; - s = work; - p = (char *)is; - while (isgraph(*p) && --n) - *s++ = *p++; - *s = '\0'; - /* it is possible that a really odd input (like lots of leading - zeros) could be truncated in copying into work. But ... */ - sign = *(s = work); - if (sign == '+' || sign == '-') s++; - else sign = '+'; - v = 0.; - for (nl = 0 ; nl < 3 ; nl = n + 1 ) { - if (!(isdigit(*s) || *s == '.')) break; - if ((tv = proj_strtod(s, &s)) == HUGE_VAL) - return tv; - switch (*s) { - case 'D': case 'd': - n = 0; break; - case '\'': - n = 1; break; - case '"': - n = 2; break; - case 'r': case 'R': - if (nl) { - pj_ctx_set_errno( ctx, PJD_ERR_WRONG_FORMAT_DMS_VALUE ); - return HUGE_VAL; - } - ++s; - v = tv; - goto skip; - default: - v += tv * vm[nl]; - skip: n = 4; - continue; - } - if (n < nl) { - pj_ctx_set_errno( ctx, PJD_ERR_WRONG_FORMAT_DMS_VALUE ); - return HUGE_VAL; - } - v += tv * vm[n]; - ++s; - } - /* postfix sign */ - if (*s && (p = strchr(sym, *s))) { - sign = (p - sym) >= 4 ? '-' : '+'; - ++s; - } - if (sign == '-') - v = -v; - if (rs) /* return point of next char after valid string */ - *rs = (char *)is + (s - work); - return v; -} - -static double -proj_strtod(char *nptr, char **endptr) - -{ - char c, *cp = nptr; - double result; - - /* - * Scan for characters which cause problems with VC++ strtod() - */ - while ((c = *cp) != '\0') { - if (c == 'd' || c == 'D') { - - /* - * Found one, so NUL it out, call strtod(), - * then restore it and return - */ - *cp = '\0'; - result = strtod(nptr, endptr); - *cp = c; - return result; - } - ++cp; - } - - /* no offending characters, just handle normally */ - - return pj_strtod(nptr, endptr); -} - diff --git a/src/dmstor.cpp b/src/dmstor.cpp new file mode 100644 index 00000000..967a9f18 --- /dev/null +++ b/src/dmstor.cpp @@ -0,0 +1,122 @@ +/* Convert DMS string to radians */ + +#include +#include +#include +#include + +#include "projects.h" + +static double proj_strtod(char *nptr, char **endptr); + +/* following should be sufficient for all but the ridiculous */ +#define MAX_WORK 64 + static const char +*sym = "NnEeSsWw"; + static const double +vm[] = { + DEG_TO_RAD, + .0002908882086657216, + .0000048481368110953599 +}; + double +dmstor(const char *is, char **rs) { + return dmstor_ctx( pj_get_default_ctx(), is, rs ); +} + + double +dmstor_ctx(projCtx ctx, const char *is, char **rs) { + int sign, n, nl; + char *s, work[MAX_WORK]; + const char* p; + double v, tv; + + if (rs) + *rs = (char *)is; + /* copy sting into work space */ + while (isspace(sign = *is)) ++is; + n = MAX_WORK; + s = work; + p = (char *)is; + while (isgraph(*p) && --n) + *s++ = *p++; + *s = '\0'; + /* it is possible that a really odd input (like lots of leading + zeros) could be truncated in copying into work. But ... */ + sign = *(s = work); + if (sign == '+' || sign == '-') s++; + else sign = '+'; + v = 0.; + for (nl = 0 ; nl < 3 ; nl = n + 1 ) { + if (!(isdigit(*s) || *s == '.')) break; + if ((tv = proj_strtod(s, &s)) == HUGE_VAL) + return tv; + switch (*s) { + case 'D': case 'd': + n = 0; break; + case '\'': + n = 1; break; + case '"': + n = 2; break; + case 'r': case 'R': + if (nl) { + pj_ctx_set_errno( ctx, PJD_ERR_WRONG_FORMAT_DMS_VALUE ); + return HUGE_VAL; + } + ++s; + v = tv; + goto skip; + default: + v += tv * vm[nl]; + skip: n = 4; + continue; + } + if (n < nl) { + pj_ctx_set_errno( ctx, PJD_ERR_WRONG_FORMAT_DMS_VALUE ); + return HUGE_VAL; + } + v += tv * vm[n]; + ++s; + } + /* postfix sign */ + if (*s && (p = strchr(sym, *s))) { + sign = (p - sym) >= 4 ? '-' : '+'; + ++s; + } + if (sign == '-') + v = -v; + if (rs) /* return point of next char after valid string */ + *rs = (char *)is + (s - work); + return v; +} + +static double +proj_strtod(char *nptr, char **endptr) + +{ + char c, *cp = nptr; + double result; + + /* + * Scan for characters which cause problems with VC++ strtod() + */ + while ((c = *cp) != '\0') { + if (c == 'd' || c == 'D') { + + /* + * Found one, so NUL it out, call strtod(), + * then restore it and return + */ + *cp = '\0'; + result = strtod(nptr, endptr); + *cp = c; + return result; + } + ++cp; + } + + /* no offending characters, just handle normally */ + + return pj_strtod(nptr, endptr); +} + diff --git a/src/emess.c b/src/emess.c deleted file mode 100644 index eb2ac9d6..00000000 --- a/src/emess.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Error message processing */ - -#ifdef _MSC_VER -# ifndef _CRT_SECURE_NO_DEPRECATE -# define _CRT_SECURE_NO_DEPRECATE -# endif -# ifndef _CRT_NONSTDC_NO_DEPRECATE -# define _CRT_NONSTDC_NO_DEPRECATE -# endif -#endif - -#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#endif - -#include -#include -#include -#include -#include - -#include "proj_api.h" -#define EMESS_ROUTINE -#include "emess.h" - - void -emess(int code, const char *fmt, ...) { - va_list args; - - va_start(args, fmt); - /* prefix program name, if given */ - if (fmt != NULL) - (void)fprintf(stderr,"%s\n<%s>: ",pj_get_release(), - emess_dat.Prog_name); - /* print file name and line, if given */ - if (emess_dat.File_name != NULL && *emess_dat.File_name) { - (void)fprintf(stderr,"while processing file: %s", emess_dat.File_name); - if (emess_dat.File_line > 0) - (void)fprintf(stderr,", line %d\n", emess_dat.File_line); - else - (void)fputc('\n', stderr); - } else - putc('\n', stderr); - /* if |code|==2, print errno code data */ - if (code == 2 || code == -2) - { - int my_errno = errno; -#ifdef HAVE_STRERROR - const char* my_strerror = strerror(my_errno); -#endif -#ifndef HAVE_STRERROR - const char* my_strerror = ""; -#endif - (void)fprintf(stderr, "Sys errno: %d: %s\n", - my_errno, my_strerror); - } - - /* post remainder of call data */ - (void)vfprintf(stderr,fmt,args); - va_end(args); - /* die if code positive */ - if (code > 0) { - (void)fputs("\nprogram abnormally terminated\n", stderr); - exit(code); - } - else - putc('\n', stderr); -} diff --git a/src/emess.cpp b/src/emess.cpp new file mode 100644 index 00000000..eb2ac9d6 --- /dev/null +++ b/src/emess.cpp @@ -0,0 +1,68 @@ +/* Error message processing */ + +#ifdef _MSC_VER +# ifndef _CRT_SECURE_NO_DEPRECATE +# define _CRT_SECURE_NO_DEPRECATE +# endif +# ifndef _CRT_NONSTDC_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +# endif +#endif + +#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#endif + +#include +#include +#include +#include +#include + +#include "proj_api.h" +#define EMESS_ROUTINE +#include "emess.h" + + void +emess(int code, const char *fmt, ...) { + va_list args; + + va_start(args, fmt); + /* prefix program name, if given */ + if (fmt != NULL) + (void)fprintf(stderr,"%s\n<%s>: ",pj_get_release(), + emess_dat.Prog_name); + /* print file name and line, if given */ + if (emess_dat.File_name != NULL && *emess_dat.File_name) { + (void)fprintf(stderr,"while processing file: %s", emess_dat.File_name); + if (emess_dat.File_line > 0) + (void)fprintf(stderr,", line %d\n", emess_dat.File_line); + else + (void)fputc('\n', stderr); + } else + putc('\n', stderr); + /* if |code|==2, print errno code data */ + if (code == 2 || code == -2) + { + int my_errno = errno; +#ifdef HAVE_STRERROR + const char* my_strerror = strerror(my_errno); +#endif +#ifndef HAVE_STRERROR + const char* my_strerror = ""; +#endif + (void)fprintf(stderr, "Sys errno: %d: %s\n", + my_errno, my_strerror); + } + + /* post remainder of call data */ + (void)vfprintf(stderr,fmt,args); + va_end(args); + /* die if code positive */ + if (code > 0) { + (void)fputs("\nprogram abnormally terminated\n", stderr); + exit(code); + } + else + putc('\n', stderr); +} diff --git a/src/gen_cheb.c b/src/gen_cheb.c deleted file mode 100644 index ab16b409..00000000 --- a/src/gen_cheb.c +++ /dev/null @@ -1,78 +0,0 @@ -/* generates 'T' option output */ -#define PJ_LIB__ -#include "projects.h" -#include -#include -#include -#include "emess.h" -#ifndef COEF_LINE_MAX -#define COEF_LINE_MAX 50 -#endif - -/* FIXME: put the declaration in a header. Also used in proj.c */ -void gen_cheb(int inverse, projUV (*proj)(projUV), char *s, PJ *P, - int iargc, char **iargv); -extern void p_series(Tseries *, FILE *, char *); - -void gen_cheb(int inverse, projUV (*proj)(projUV), char *s, PJ *P, - int iargc, char **iargv) { - long NU = 15, NV = 15; - int errin = 0, pwr; - long res = -1; - char *arg, fmt[32]; - projUV low, upp, resid; - Tseries *F; - double (*input)(const char *, char **); - - input = inverse ? strtod : dmstor; - if (*s) low.u = input(s, &s); else { low.u = 0; ++errin; } - if (*s == ',') upp.u = input(s+1, &s); else { upp.u = 0; ++errin; } - if (*s == ',') low.v = input(s+1, &s); else { low.v = 0; ++errin; } - if (*s == ',') upp.v = input(s+1, &s); else { upp.v = 0; ++errin; } - if (errin) - emess(16,"null or absent -T parameters"); - if (*s == ',') if (*++s != ',') res = strtol(s, &s, 10); - if (*s == ',') if (*++s != ',') NU = strtol(s, &s, 10); - if (*s == ',') if (*++s != ',') NV = strtol(s, &s, 10); - pwr = s && *s && !strcmp(s, ",P"); - (void)printf("#proj_%s\n# run-line:\n", - pwr ? "Power" : "Chebyshev"); - if (iargc > 0) { /* proj execution audit trail */ - int n = 0, L; - - for ( ; iargc ; --iargc) { - arg = *iargv++; - if (*arg != '+') { - if (!n) { putchar('#'); ++n; } - (void)printf(" %s%n",arg, &L); - if ((n += L) > COEF_LINE_MAX) { putchar('\n'); n = 0; } - } - } - if (n) putchar('\n'); - } - (void)printf("# projection parameters\n"); - pj_pr_list(P); - if (low.u == upp.u || low.v >= upp.v) - emess(16,"approx. argument range error"); - if (low.u > upp.u) - low.u -= M_TWOPI; - if (NU < 2 || NV < 2 || NU > INT_MAX || NV > INT_MAX) - emess(16,"approx. work dimensions (%ld %ld) too small or large",NU,NV); - if (!(F = mk_cheby(low, upp, pow(10., (double)res)*.5, &resid, proj, - (int)NU, (int)NV, pwr))) - emess(16,"generation of approx failed\nreason: %s\n", - pj_strerrno(errno)); - (void)printf("%c,%.12g,%.12g,%.12g,%.12g,%.12g\n",inverse?'I':'F', - P->lam0*RAD_TO_DEG, - low.u*(inverse?1.:RAD_TO_DEG),upp.u*(inverse?1.:RAD_TO_DEG), - low.v*(inverse?1.:RAD_TO_DEG),upp.v*(inverse?1.:RAD_TO_DEG)); - if (pwr) - strcpy(fmt, "%.15g"); - else if (res <= 0) - (void)sprintf(fmt,"%%.%ldf",-res+1); - else - (void)strcpy(fmt,"%.0f"); - p_series(F, stdout, fmt); - (void)printf("# |u,v| sums %g %g\n#end_proj_%s\n", - resid.u, resid.v, pwr ? "Power" : "Chebyshev"); -} diff --git a/src/gen_cheb.cpp b/src/gen_cheb.cpp new file mode 100644 index 00000000..ab16b409 --- /dev/null +++ b/src/gen_cheb.cpp @@ -0,0 +1,78 @@ +/* generates 'T' option output */ +#define PJ_LIB__ +#include "projects.h" +#include +#include +#include +#include "emess.h" +#ifndef COEF_LINE_MAX +#define COEF_LINE_MAX 50 +#endif + +/* FIXME: put the declaration in a header. Also used in proj.c */ +void gen_cheb(int inverse, projUV (*proj)(projUV), char *s, PJ *P, + int iargc, char **iargv); +extern void p_series(Tseries *, FILE *, char *); + +void gen_cheb(int inverse, projUV (*proj)(projUV), char *s, PJ *P, + int iargc, char **iargv) { + long NU = 15, NV = 15; + int errin = 0, pwr; + long res = -1; + char *arg, fmt[32]; + projUV low, upp, resid; + Tseries *F; + double (*input)(const char *, char **); + + input = inverse ? strtod : dmstor; + if (*s) low.u = input(s, &s); else { low.u = 0; ++errin; } + if (*s == ',') upp.u = input(s+1, &s); else { upp.u = 0; ++errin; } + if (*s == ',') low.v = input(s+1, &s); else { low.v = 0; ++errin; } + if (*s == ',') upp.v = input(s+1, &s); else { upp.v = 0; ++errin; } + if (errin) + emess(16,"null or absent -T parameters"); + if (*s == ',') if (*++s != ',') res = strtol(s, &s, 10); + if (*s == ',') if (*++s != ',') NU = strtol(s, &s, 10); + if (*s == ',') if (*++s != ',') NV = strtol(s, &s, 10); + pwr = s && *s && !strcmp(s, ",P"); + (void)printf("#proj_%s\n# run-line:\n", + pwr ? "Power" : "Chebyshev"); + if (iargc > 0) { /* proj execution audit trail */ + int n = 0, L; + + for ( ; iargc ; --iargc) { + arg = *iargv++; + if (*arg != '+') { + if (!n) { putchar('#'); ++n; } + (void)printf(" %s%n",arg, &L); + if ((n += L) > COEF_LINE_MAX) { putchar('\n'); n = 0; } + } + } + if (n) putchar('\n'); + } + (void)printf("# projection parameters\n"); + pj_pr_list(P); + if (low.u == upp.u || low.v >= upp.v) + emess(16,"approx. argument range error"); + if (low.u > upp.u) + low.u -= M_TWOPI; + if (NU < 2 || NV < 2 || NU > INT_MAX || NV > INT_MAX) + emess(16,"approx. work dimensions (%ld %ld) too small or large",NU,NV); + if (!(F = mk_cheby(low, upp, pow(10., (double)res)*.5, &resid, proj, + (int)NU, (int)NV, pwr))) + emess(16,"generation of approx failed\nreason: %s\n", + pj_strerrno(errno)); + (void)printf("%c,%.12g,%.12g,%.12g,%.12g,%.12g\n",inverse?'I':'F', + P->lam0*RAD_TO_DEG, + low.u*(inverse?1.:RAD_TO_DEG),upp.u*(inverse?1.:RAD_TO_DEG), + low.v*(inverse?1.:RAD_TO_DEG),upp.v*(inverse?1.:RAD_TO_DEG)); + if (pwr) + strcpy(fmt, "%.15g"); + else if (res <= 0) + (void)sprintf(fmt,"%%.%ldf",-res+1); + else + (void)strcpy(fmt,"%.0f"); + p_series(F, stdout, fmt); + (void)printf("# |u,v| sums %g %g\n#end_proj_%s\n", + resid.u, resid.v, pwr ? "Power" : "Chebyshev"); +} diff --git a/src/geocent.c b/src/geocent.c deleted file mode 100644 index c023bdd3..00000000 --- a/src/geocent.c +++ /dev/null @@ -1,436 +0,0 @@ -/***************************************************************************/ -/* RSC IDENTIFIER: GEOCENTRIC - * - * ABSTRACT - * - * This component provides conversions between Geodetic coordinates (latitude, - * longitude in radians and height in meters) and Geocentric coordinates - * (X, Y, Z) in meters. - * - * ERROR HANDLING - * - * This component checks parameters for valid values. If an invalid value - * is found, the error code is combined with the current error code using - * the bitwise or. This combining allows multiple error codes to be - * returned. The possible error codes are: - * - * GEOCENT_NO_ERROR : No errors occurred in function - * GEOCENT_LAT_ERROR : Latitude out of valid range - * (-90 to 90 degrees) - * GEOCENT_LON_ERROR : Longitude out of valid range - * (-180 to 360 degrees) - * GEOCENT_A_ERROR : Semi-major axis lessthan or equal to zero - * GEOCENT_B_ERROR : Semi-minor axis lessthan or equal to zero - * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis - * - * - * REUSE NOTES - * - * GEOCENTRIC is intended for reuse by any application that performs - * coordinate conversions between geodetic coordinates and geocentric - * coordinates. - * - * - * REFERENCES - * - * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion, - * Ralph Toms, February 1996 UCRL-JC-123138. - * - * Further information on GEOCENTRIC can be found in the Reuse Manual. - * - * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center - * Geospatial Information Division - * 7701 Telegraph Road - * Alexandria, VA 22310-3864 - * - * LICENSES - * - * None apply to this component. - * - * RESTRICTIONS - * - * GEOCENTRIC has no restrictions. - * - * ENVIRONMENT - * - * GEOCENTRIC was tested and certified in the following environments: - * - * 1. Solaris 2.5 with GCC version 2.8.1 - * 2. Windows 95 with MS Visual C++ version 6 - * - * MODIFICATIONS - * - * Date Description - * ---- ----------- - * 25-02-97 Original Code - * - */ - - -/***************************************************************************/ -/* - * INCLUDES - */ -#include -#include "geocent.h" -/* - * math.h - is needed for calls to sin, cos, tan and sqrt. - * geocent.h - is needed for Error codes and prototype error checking. - */ - - -/***************************************************************************/ -/* - * DEFINES - */ -#define PI 3.14159265358979323e0 -#define PI_OVER_2 (PI / 2.0e0) -#define FALSE 0 -#define TRUE 1 -#define COS_67P5 0.38268343236508977 /* cosine of 67.5 degrees */ -#define AD_C 1.0026000 /* Toms region 1 constant */ - - -/***************************************************************************/ -/* - * FUNCTIONS - */ - - -long pj_Set_Geocentric_Parameters (GeocentricInfo *gi, double a, double b) - -{ /* BEGIN Set_Geocentric_Parameters */ -/* - * The function Set_Geocentric_Parameters receives the ellipsoid parameters - * as inputs and sets the corresponding state variables. - * - * a : Semi-major axis, in meters. (input) - * b : Semi-minor axis, in meters. (input) - */ - long Error_Code = GEOCENT_NO_ERROR; - - if (a <= 0.0) - Error_Code |= GEOCENT_A_ERROR; - if (b <= 0.0) - Error_Code |= GEOCENT_B_ERROR; - if (a < b) - Error_Code |= GEOCENT_A_LESS_B_ERROR; - if (!Error_Code) - { - gi->Geocent_a = a; - gi->Geocent_b = b; - gi->Geocent_a2 = a * a; - gi->Geocent_b2 = b * b; - gi->Geocent_e2 = (gi->Geocent_a2 - gi->Geocent_b2) / gi->Geocent_a2; - gi->Geocent_ep2 = (gi->Geocent_a2 - gi->Geocent_b2) / gi->Geocent_b2; - } - return (Error_Code); -} /* END OF Set_Geocentric_Parameters */ - - -void pj_Get_Geocentric_Parameters (GeocentricInfo *gi, - double *a, - double *b) -{ /* BEGIN Get_Geocentric_Parameters */ -/* - * The function Get_Geocentric_Parameters returns the ellipsoid parameters - * to be used in geocentric coordinate conversions. - * - * a : Semi-major axis, in meters. (output) - * b : Semi-minor axis, in meters. (output) - */ - - *a = gi->Geocent_a; - *b = gi->Geocent_b; -} /* END OF Get_Geocentric_Parameters */ - - -long pj_Convert_Geodetic_To_Geocentric (GeocentricInfo *gi, - double Latitude, - double Longitude, - double Height, - double *X, - double *Y, - double *Z) -{ /* BEGIN Convert_Geodetic_To_Geocentric */ -/* - * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates - * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z), - * according to the current ellipsoid parameters. - * - * Latitude : Geodetic latitude in radians (input) - * Longitude : Geodetic longitude in radians (input) - * Height : Geodetic height, in meters (input) - * X : Calculated Geocentric X coordinate, in meters (output) - * Y : Calculated Geocentric Y coordinate, in meters (output) - * Z : Calculated Geocentric Z coordinate, in meters (output) - * - */ - long Error_Code = GEOCENT_NO_ERROR; - double Rn; /* Earth radius at location */ - double Sin_Lat; /* sin(Latitude) */ - double Sin2_Lat; /* Square of sin(Latitude) */ - double Cos_Lat; /* cos(Latitude) */ - - /* - ** Don't blow up if Latitude is just a little out of the value - ** range as it may just be a rounding issue. Also removed longitude - ** test, it should be wrapped by cos() and sin(). NFW for PROJ.4, Sep/2001. - */ - if( Latitude < -PI_OVER_2 && Latitude > -1.001 * PI_OVER_2 ) - Latitude = -PI_OVER_2; - else if( Latitude > PI_OVER_2 && Latitude < 1.001 * PI_OVER_2 ) - Latitude = PI_OVER_2; - else if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2)) - { /* Latitude out of range */ - Error_Code |= GEOCENT_LAT_ERROR; - } - - if (!Error_Code) - { /* no errors */ - if (Longitude > PI) - Longitude -= (2*PI); - Sin_Lat = sin(Latitude); - Cos_Lat = cos(Latitude); - Sin2_Lat = Sin_Lat * Sin_Lat; - Rn = gi->Geocent_a / (sqrt(1.0e0 - gi->Geocent_e2 * Sin2_Lat)); - *X = (Rn + Height) * Cos_Lat * cos(Longitude); - *Y = (Rn + Height) * Cos_Lat * sin(Longitude); - *Z = ((Rn * (1 - gi->Geocent_e2)) + Height) * Sin_Lat; - - } - return (Error_Code); -} /* END OF Convert_Geodetic_To_Geocentric */ - -/* - * The function Convert_Geocentric_To_Geodetic converts geocentric - * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude, - * and height), according to the current ellipsoid parameters. - * - * X : Geocentric X coordinate, in meters. (input) - * Y : Geocentric Y coordinate, in meters. (input) - * Z : Geocentric Z coordinate, in meters. (input) - * Latitude : Calculated latitude value in radians. (output) - * Longitude : Calculated longitude value in radians. (output) - * Height : Calculated height value, in meters. (output) - */ - -#define USE_ITERATIVE_METHOD - -void pj_Convert_Geocentric_To_Geodetic (GeocentricInfo *gi, - double X, - double Y, - double Z, - double *Latitude, - double *Longitude, - double *Height) -{ /* BEGIN Convert_Geocentric_To_Geodetic */ -#if !defined(USE_ITERATIVE_METHOD) -/* - * The method used here is derived from 'An Improved Algorithm for - * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996 - */ - -/* Note: Variable names follow the notation used in Toms, Feb 1996 */ - - double W; /* distance from Z axis */ - double W2; /* square of distance from Z axis */ - double T0; /* initial estimate of vertical component */ - double T1; /* corrected estimate of vertical component */ - double S0; /* initial estimate of horizontal component */ - double S1; /* corrected estimate of horizontal component */ - double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */ - double Sin3_B0; /* cube of sin(B0) */ - double Cos_B0; /* cos(B0) */ - double Sin_p1; /* sin(phi1), phi1 is estimated latitude */ - double Cos_p1; /* cos(phi1) */ - double Rn; /* Earth radius at location */ - double Sum; /* numerator of cos(phi1) */ - int At_Pole; /* indicates location is in polar region */ - - At_Pole = FALSE; - if (X != 0.0) - { - *Longitude = atan2(Y,X); - } - else - { - if (Y > 0) - { - *Longitude = PI_OVER_2; - } - else if (Y < 0) - { - *Longitude = -PI_OVER_2; - } - else - { - At_Pole = TRUE; - *Longitude = 0.0; - if (Z > 0.0) - { /* north pole */ - *Latitude = PI_OVER_2; - } - else if (Z < 0.0) - { /* south pole */ - *Latitude = -PI_OVER_2; - } - else - { /* center of earth */ - *Latitude = PI_OVER_2; - *Height = -Geocent_b; - return; - } - } - } - W2 = X*X + Y*Y; - W = sqrt(W2); - T0 = Z * AD_C; - S0 = sqrt(T0 * T0 + W2); - Sin_B0 = T0 / S0; - Cos_B0 = W / S0; - Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0; - T1 = Z + gi->Geocent_b * gi->Geocent_ep2 * Sin3_B0; - Sum = W - gi->Geocent_a * gi->Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0; - S1 = sqrt(T1*T1 + Sum * Sum); - Sin_p1 = T1 / S1; - Cos_p1 = Sum / S1; - Rn = gi->Geocent_a / sqrt(1.0 - gi->Geocent_e2 * Sin_p1 * Sin_p1); - if (Cos_p1 >= COS_67P5) - { - *Height = W / Cos_p1 - Rn; - } - else if (Cos_p1 <= -COS_67P5) - { - *Height = W / -Cos_p1 - Rn; - } - else - { - *Height = Z / Sin_p1 + Rn * (gi->Geocent_e2 - 1.0); - } - if (At_Pole == FALSE) - { - *Latitude = atan(Sin_p1 / Cos_p1); - } -#else /* defined(USE_ITERATIVE_METHOD) */ -/* -* Reference... -* ============ -* Wenzel, H.-G.(1985): Hochauflösende Kugelfunktionsmodelle für -* das Gravitationspotential der Erde. Wiss. Arb. Univ. Hannover -* Nr. 137, p. 130-131. - -* Programmed by GGA- Leibniz-Institute of Applied Geophysics -* Stilleweg 2 -* D-30655 Hannover -* Federal Republic of Germany -* Internet: www.gga-hannover.de -* -* Hannover, March 1999, April 2004. -* see also: comments in statements -* remarks: -* Mathematically exact and because of symmetry of rotation-ellipsoid, -* each point (X,Y,Z) has at least two solutions (Latitude1,Longitude1,Height1) and -* (Latitude2,Longitude2,Height2). Is point=(0.,0.,Z) (P=0.), so you get even -* four solutions, every two symmetrical to the semi-minor axis. -* Here Height1 and Height2 have at least a difference in order of -* radius of curvature (e.g. (0,0,b)=> (90.,0.,0.) or (-90.,0.,-2b); -* (a+100.)*(sqrt(2.)/2.,sqrt(2.)/2.,0.) => (0.,45.,100.) or -* (0.,225.,-(2a+100.))). -* The algorithm always computes (Latitude,Longitude) with smallest |Height|. -* For normal computations, that means |Height|<10000.m, algorithm normally -* converges after to 2-3 steps!!! -* But if |Height| has the amount of length of ellipsoid's axis -* (e.g. -6300000.m), algorithm needs about 15 steps. -*/ - -/* local definitions and variables */ -/* end-criterium of loop, accuracy of sin(Latitude) */ -#define genau 1.E-12 -#define genau2 (genau*genau) -#define maxiter 30 - - double P; /* distance between semi-minor axis and location */ - double RR; /* distance between center and location */ - double CT; /* sin of geocentric latitude */ - double ST; /* cos of geocentric latitude */ - double RX; - double RK; - double RN; /* Earth radius at location */ - double CPHI0; /* cos of start or old geodetic latitude in iterations */ - double SPHI0; /* sin of start or old geodetic latitude in iterations */ - double CPHI; /* cos of searched geodetic latitude */ - double SPHI; /* sin of searched geodetic latitude */ - double SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */ - int iter; /* # of continuous iteration, max. 30 is always enough (s.a.) */ - - P = sqrt(X*X+Y*Y); - RR = sqrt(X*X+Y*Y+Z*Z); - -/* special cases for latitude and longitude */ - if (P/gi->Geocent_a < genau) { - -/* special case, if P=0. (X=0., Y=0.) */ - *Longitude = 0.; - -/* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis - * of ellipsoid (=center of mass), Latitude becomes PI/2 */ - if (RR/gi->Geocent_a < genau) { - *Latitude = PI_OVER_2; - *Height = -gi->Geocent_b; - return ; - - } - } - else { -/* ellipsoidal (geodetic) longitude - * interval: -PI < Longitude <= +PI */ - *Longitude=atan2(Y,X); - } - -/* -------------------------------------------------------------- - * Following iterative algorithm was developed by - * "Institut für Erdmessung", University of Hannover, July 1988. - * Internet: www.ife.uni-hannover.de - * Iterative computation of CPHI,SPHI and Height. - * Iteration of CPHI and SPHI to 10**-12 radian resp. - * 2*10**-7 arcsec. - * -------------------------------------------------------------- - */ - CT = Z/RR; - ST = P/RR; - RX = 1.0/sqrt(1.0-gi->Geocent_e2*(2.0-gi->Geocent_e2)*ST*ST); - CPHI0 = ST*(1.0-gi->Geocent_e2)*RX; - SPHI0 = CT*RX; - iter = 0; - -/* loop to find sin(Latitude) resp. Latitude - * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */ - do - { - iter++; - RN = gi->Geocent_a/sqrt(1.0-gi->Geocent_e2*SPHI0*SPHI0); - -/* ellipsoidal (geodetic) height */ - *Height = P*CPHI0+Z*SPHI0-RN*(1.0-gi->Geocent_e2*SPHI0*SPHI0); - - /* avoid zero division */ - if (RN+*Height==0.0) { - *Latitude = 0.0; - return; - } - RK = gi->Geocent_e2*RN/(RN+*Height); - RX = 1.0/sqrt(1.0-RK*(2.0-RK)*ST*ST); - CPHI = ST*(1.0-RK)*RX; - SPHI = CT*RX; - SDPHI = SPHI*CPHI0-CPHI*SPHI0; - CPHI0 = CPHI; - SPHI0 = SPHI; - } - while (SDPHI*SDPHI > genau2 && iter < maxiter); - -/* ellipsoidal (geodetic) latitude */ - *Latitude=atan2(SPHI, fabs(CPHI)); - -#endif /* defined(USE_ITERATIVE_METHOD) */ -} /* END OF Convert_Geocentric_To_Geodetic */ diff --git a/src/geocent.cpp b/src/geocent.cpp new file mode 100644 index 00000000..c023bdd3 --- /dev/null +++ b/src/geocent.cpp @@ -0,0 +1,436 @@ +/***************************************************************************/ +/* RSC IDENTIFIER: GEOCENTRIC + * + * ABSTRACT + * + * This component provides conversions between Geodetic coordinates (latitude, + * longitude in radians and height in meters) and Geocentric coordinates + * (X, Y, Z) in meters. + * + * ERROR HANDLING + * + * This component checks parameters for valid values. If an invalid value + * is found, the error code is combined with the current error code using + * the bitwise or. This combining allows multiple error codes to be + * returned. The possible error codes are: + * + * GEOCENT_NO_ERROR : No errors occurred in function + * GEOCENT_LAT_ERROR : Latitude out of valid range + * (-90 to 90 degrees) + * GEOCENT_LON_ERROR : Longitude out of valid range + * (-180 to 360 degrees) + * GEOCENT_A_ERROR : Semi-major axis lessthan or equal to zero + * GEOCENT_B_ERROR : Semi-minor axis lessthan or equal to zero + * GEOCENT_A_LESS_B_ERROR : Semi-major axis less than semi-minor axis + * + * + * REUSE NOTES + * + * GEOCENTRIC is intended for reuse by any application that performs + * coordinate conversions between geodetic coordinates and geocentric + * coordinates. + * + * + * REFERENCES + * + * An Improved Algorithm for Geocentric to Geodetic Coordinate Conversion, + * Ralph Toms, February 1996 UCRL-JC-123138. + * + * Further information on GEOCENTRIC can be found in the Reuse Manual. + * + * GEOCENTRIC originated from : U.S. Army Topographic Engineering Center + * Geospatial Information Division + * 7701 Telegraph Road + * Alexandria, VA 22310-3864 + * + * LICENSES + * + * None apply to this component. + * + * RESTRICTIONS + * + * GEOCENTRIC has no restrictions. + * + * ENVIRONMENT + * + * GEOCENTRIC was tested and certified in the following environments: + * + * 1. Solaris 2.5 with GCC version 2.8.1 + * 2. Windows 95 with MS Visual C++ version 6 + * + * MODIFICATIONS + * + * Date Description + * ---- ----------- + * 25-02-97 Original Code + * + */ + + +/***************************************************************************/ +/* + * INCLUDES + */ +#include +#include "geocent.h" +/* + * math.h - is needed for calls to sin, cos, tan and sqrt. + * geocent.h - is needed for Error codes and prototype error checking. + */ + + +/***************************************************************************/ +/* + * DEFINES + */ +#define PI 3.14159265358979323e0 +#define PI_OVER_2 (PI / 2.0e0) +#define FALSE 0 +#define TRUE 1 +#define COS_67P5 0.38268343236508977 /* cosine of 67.5 degrees */ +#define AD_C 1.0026000 /* Toms region 1 constant */ + + +/***************************************************************************/ +/* + * FUNCTIONS + */ + + +long pj_Set_Geocentric_Parameters (GeocentricInfo *gi, double a, double b) + +{ /* BEGIN Set_Geocentric_Parameters */ +/* + * The function Set_Geocentric_Parameters receives the ellipsoid parameters + * as inputs and sets the corresponding state variables. + * + * a : Semi-major axis, in meters. (input) + * b : Semi-minor axis, in meters. (input) + */ + long Error_Code = GEOCENT_NO_ERROR; + + if (a <= 0.0) + Error_Code |= GEOCENT_A_ERROR; + if (b <= 0.0) + Error_Code |= GEOCENT_B_ERROR; + if (a < b) + Error_Code |= GEOCENT_A_LESS_B_ERROR; + if (!Error_Code) + { + gi->Geocent_a = a; + gi->Geocent_b = b; + gi->Geocent_a2 = a * a; + gi->Geocent_b2 = b * b; + gi->Geocent_e2 = (gi->Geocent_a2 - gi->Geocent_b2) / gi->Geocent_a2; + gi->Geocent_ep2 = (gi->Geocent_a2 - gi->Geocent_b2) / gi->Geocent_b2; + } + return (Error_Code); +} /* END OF Set_Geocentric_Parameters */ + + +void pj_Get_Geocentric_Parameters (GeocentricInfo *gi, + double *a, + double *b) +{ /* BEGIN Get_Geocentric_Parameters */ +/* + * The function Get_Geocentric_Parameters returns the ellipsoid parameters + * to be used in geocentric coordinate conversions. + * + * a : Semi-major axis, in meters. (output) + * b : Semi-minor axis, in meters. (output) + */ + + *a = gi->Geocent_a; + *b = gi->Geocent_b; +} /* END OF Get_Geocentric_Parameters */ + + +long pj_Convert_Geodetic_To_Geocentric (GeocentricInfo *gi, + double Latitude, + double Longitude, + double Height, + double *X, + double *Y, + double *Z) +{ /* BEGIN Convert_Geodetic_To_Geocentric */ +/* + * The function Convert_Geodetic_To_Geocentric converts geodetic coordinates + * (latitude, longitude, and height) to geocentric coordinates (X, Y, Z), + * according to the current ellipsoid parameters. + * + * Latitude : Geodetic latitude in radians (input) + * Longitude : Geodetic longitude in radians (input) + * Height : Geodetic height, in meters (input) + * X : Calculated Geocentric X coordinate, in meters (output) + * Y : Calculated Geocentric Y coordinate, in meters (output) + * Z : Calculated Geocentric Z coordinate, in meters (output) + * + */ + long Error_Code = GEOCENT_NO_ERROR; + double Rn; /* Earth radius at location */ + double Sin_Lat; /* sin(Latitude) */ + double Sin2_Lat; /* Square of sin(Latitude) */ + double Cos_Lat; /* cos(Latitude) */ + + /* + ** Don't blow up if Latitude is just a little out of the value + ** range as it may just be a rounding issue. Also removed longitude + ** test, it should be wrapped by cos() and sin(). NFW for PROJ.4, Sep/2001. + */ + if( Latitude < -PI_OVER_2 && Latitude > -1.001 * PI_OVER_2 ) + Latitude = -PI_OVER_2; + else if( Latitude > PI_OVER_2 && Latitude < 1.001 * PI_OVER_2 ) + Latitude = PI_OVER_2; + else if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2)) + { /* Latitude out of range */ + Error_Code |= GEOCENT_LAT_ERROR; + } + + if (!Error_Code) + { /* no errors */ + if (Longitude > PI) + Longitude -= (2*PI); + Sin_Lat = sin(Latitude); + Cos_Lat = cos(Latitude); + Sin2_Lat = Sin_Lat * Sin_Lat; + Rn = gi->Geocent_a / (sqrt(1.0e0 - gi->Geocent_e2 * Sin2_Lat)); + *X = (Rn + Height) * Cos_Lat * cos(Longitude); + *Y = (Rn + Height) * Cos_Lat * sin(Longitude); + *Z = ((Rn * (1 - gi->Geocent_e2)) + Height) * Sin_Lat; + + } + return (Error_Code); +} /* END OF Convert_Geodetic_To_Geocentric */ + +/* + * The function Convert_Geocentric_To_Geodetic converts geocentric + * coordinates (X, Y, Z) to geodetic coordinates (latitude, longitude, + * and height), according to the current ellipsoid parameters. + * + * X : Geocentric X coordinate, in meters. (input) + * Y : Geocentric Y coordinate, in meters. (input) + * Z : Geocentric Z coordinate, in meters. (input) + * Latitude : Calculated latitude value in radians. (output) + * Longitude : Calculated longitude value in radians. (output) + * Height : Calculated height value, in meters. (output) + */ + +#define USE_ITERATIVE_METHOD + +void pj_Convert_Geocentric_To_Geodetic (GeocentricInfo *gi, + double X, + double Y, + double Z, + double *Latitude, + double *Longitude, + double *Height) +{ /* BEGIN Convert_Geocentric_To_Geodetic */ +#if !defined(USE_ITERATIVE_METHOD) +/* + * The method used here is derived from 'An Improved Algorithm for + * Geocentric to Geodetic Coordinate Conversion', by Ralph Toms, Feb 1996 + */ + +/* Note: Variable names follow the notation used in Toms, Feb 1996 */ + + double W; /* distance from Z axis */ + double W2; /* square of distance from Z axis */ + double T0; /* initial estimate of vertical component */ + double T1; /* corrected estimate of vertical component */ + double S0; /* initial estimate of horizontal component */ + double S1; /* corrected estimate of horizontal component */ + double Sin_B0; /* sin(B0), B0 is estimate of Bowring aux variable */ + double Sin3_B0; /* cube of sin(B0) */ + double Cos_B0; /* cos(B0) */ + double Sin_p1; /* sin(phi1), phi1 is estimated latitude */ + double Cos_p1; /* cos(phi1) */ + double Rn; /* Earth radius at location */ + double Sum; /* numerator of cos(phi1) */ + int At_Pole; /* indicates location is in polar region */ + + At_Pole = FALSE; + if (X != 0.0) + { + *Longitude = atan2(Y,X); + } + else + { + if (Y > 0) + { + *Longitude = PI_OVER_2; + } + else if (Y < 0) + { + *Longitude = -PI_OVER_2; + } + else + { + At_Pole = TRUE; + *Longitude = 0.0; + if (Z > 0.0) + { /* north pole */ + *Latitude = PI_OVER_2; + } + else if (Z < 0.0) + { /* south pole */ + *Latitude = -PI_OVER_2; + } + else + { /* center of earth */ + *Latitude = PI_OVER_2; + *Height = -Geocent_b; + return; + } + } + } + W2 = X*X + Y*Y; + W = sqrt(W2); + T0 = Z * AD_C; + S0 = sqrt(T0 * T0 + W2); + Sin_B0 = T0 / S0; + Cos_B0 = W / S0; + Sin3_B0 = Sin_B0 * Sin_B0 * Sin_B0; + T1 = Z + gi->Geocent_b * gi->Geocent_ep2 * Sin3_B0; + Sum = W - gi->Geocent_a * gi->Geocent_e2 * Cos_B0 * Cos_B0 * Cos_B0; + S1 = sqrt(T1*T1 + Sum * Sum); + Sin_p1 = T1 / S1; + Cos_p1 = Sum / S1; + Rn = gi->Geocent_a / sqrt(1.0 - gi->Geocent_e2 * Sin_p1 * Sin_p1); + if (Cos_p1 >= COS_67P5) + { + *Height = W / Cos_p1 - Rn; + } + else if (Cos_p1 <= -COS_67P5) + { + *Height = W / -Cos_p1 - Rn; + } + else + { + *Height = Z / Sin_p1 + Rn * (gi->Geocent_e2 - 1.0); + } + if (At_Pole == FALSE) + { + *Latitude = atan(Sin_p1 / Cos_p1); + } +#else /* defined(USE_ITERATIVE_METHOD) */ +/* +* Reference... +* ============ +* Wenzel, H.-G.(1985): Hochauflösende Kugelfunktionsmodelle für +* das Gravitationspotential der Erde. Wiss. Arb. Univ. Hannover +* Nr. 137, p. 130-131. + +* Programmed by GGA- Leibniz-Institute of Applied Geophysics +* Stilleweg 2 +* D-30655 Hannover +* Federal Republic of Germany +* Internet: www.gga-hannover.de +* +* Hannover, March 1999, April 2004. +* see also: comments in statements +* remarks: +* Mathematically exact and because of symmetry of rotation-ellipsoid, +* each point (X,Y,Z) has at least two solutions (Latitude1,Longitude1,Height1) and +* (Latitude2,Longitude2,Height2). Is point=(0.,0.,Z) (P=0.), so you get even +* four solutions, every two symmetrical to the semi-minor axis. +* Here Height1 and Height2 have at least a difference in order of +* radius of curvature (e.g. (0,0,b)=> (90.,0.,0.) or (-90.,0.,-2b); +* (a+100.)*(sqrt(2.)/2.,sqrt(2.)/2.,0.) => (0.,45.,100.) or +* (0.,225.,-(2a+100.))). +* The algorithm always computes (Latitude,Longitude) with smallest |Height|. +* For normal computations, that means |Height|<10000.m, algorithm normally +* converges after to 2-3 steps!!! +* But if |Height| has the amount of length of ellipsoid's axis +* (e.g. -6300000.m), algorithm needs about 15 steps. +*/ + +/* local definitions and variables */ +/* end-criterium of loop, accuracy of sin(Latitude) */ +#define genau 1.E-12 +#define genau2 (genau*genau) +#define maxiter 30 + + double P; /* distance between semi-minor axis and location */ + double RR; /* distance between center and location */ + double CT; /* sin of geocentric latitude */ + double ST; /* cos of geocentric latitude */ + double RX; + double RK; + double RN; /* Earth radius at location */ + double CPHI0; /* cos of start or old geodetic latitude in iterations */ + double SPHI0; /* sin of start or old geodetic latitude in iterations */ + double CPHI; /* cos of searched geodetic latitude */ + double SPHI; /* sin of searched geodetic latitude */ + double SDPHI; /* end-criterium: addition-theorem of sin(Latitude(iter)-Latitude(iter-1)) */ + int iter; /* # of continuous iteration, max. 30 is always enough (s.a.) */ + + P = sqrt(X*X+Y*Y); + RR = sqrt(X*X+Y*Y+Z*Z); + +/* special cases for latitude and longitude */ + if (P/gi->Geocent_a < genau) { + +/* special case, if P=0. (X=0., Y=0.) */ + *Longitude = 0.; + +/* if (X,Y,Z)=(0.,0.,0.) then Height becomes semi-minor axis + * of ellipsoid (=center of mass), Latitude becomes PI/2 */ + if (RR/gi->Geocent_a < genau) { + *Latitude = PI_OVER_2; + *Height = -gi->Geocent_b; + return ; + + } + } + else { +/* ellipsoidal (geodetic) longitude + * interval: -PI < Longitude <= +PI */ + *Longitude=atan2(Y,X); + } + +/* -------------------------------------------------------------- + * Following iterative algorithm was developed by + * "Institut für Erdmessung", University of Hannover, July 1988. + * Internet: www.ife.uni-hannover.de + * Iterative computation of CPHI,SPHI and Height. + * Iteration of CPHI and SPHI to 10**-12 radian resp. + * 2*10**-7 arcsec. + * -------------------------------------------------------------- + */ + CT = Z/RR; + ST = P/RR; + RX = 1.0/sqrt(1.0-gi->Geocent_e2*(2.0-gi->Geocent_e2)*ST*ST); + CPHI0 = ST*(1.0-gi->Geocent_e2)*RX; + SPHI0 = CT*RX; + iter = 0; + +/* loop to find sin(Latitude) resp. Latitude + * until |sin(Latitude(iter)-Latitude(iter-1))| < genau */ + do + { + iter++; + RN = gi->Geocent_a/sqrt(1.0-gi->Geocent_e2*SPHI0*SPHI0); + +/* ellipsoidal (geodetic) height */ + *Height = P*CPHI0+Z*SPHI0-RN*(1.0-gi->Geocent_e2*SPHI0*SPHI0); + + /* avoid zero division */ + if (RN+*Height==0.0) { + *Latitude = 0.0; + return; + } + RK = gi->Geocent_e2*RN/(RN+*Height); + RX = 1.0/sqrt(1.0-RK*(2.0-RK)*ST*ST); + CPHI = ST*(1.0-RK)*RX; + SPHI = CT*RX; + SDPHI = SPHI*CPHI0-CPHI*SPHI0; + CPHI0 = CPHI; + SPHI0 = SPHI; + } + while (SDPHI*SDPHI > genau2 && iter < maxiter); + +/* ellipsoidal (geodetic) latitude */ + *Latitude=atan2(SPHI, fabs(CPHI)); + +#endif /* defined(USE_ITERATIVE_METHOD) */ +} /* END OF Convert_Geocentric_To_Geodetic */ diff --git a/src/geod.c b/src/geod.c deleted file mode 100644 index bb52818e..00000000 --- a/src/geod.c +++ /dev/null @@ -1,239 +0,0 @@ -/* <<<< Geodesic filter program >>>> */ - -#include "proj.h" -# include "projects.h" -# include "geod_interface.h" -# include "emess.h" -# include -# include -# include - -# define MAXLINE 200 -# define MAX_PARGS 50 -# define TAB putchar('\t') - static int -fullout = 0, /* output full set of geodesic values */ -tag = '#', /* beginning of line tag character */ -pos_azi = 0, /* output azimuths as positive values */ -inverse = 0; /* != 0 then inverse geodesic */ - static char -*oform = (char *)0, /* output format for decimal degrees */ -*osform = "%.3f", /* output format for S */ -pline[50], /* work string */ -*usage = -"%s\nusage: %s [ -afFIlptwW [args] ] [ +opts[=arg] ] [ files ]\n"; - static void -printLL(double p, double l) { - if (oform) { - (void)printf(oform, p * RAD_TO_DEG); TAB; - (void)printf(oform, l * RAD_TO_DEG); - } else { - (void)fputs(rtodms(pline, p, 'N', 'S'),stdout); TAB; - (void)fputs(rtodms(pline, l, 'E', 'W'),stdout); - } -} - static void -do_arc(void) { - double az; - - printLL(phi2, lam2); putchar('\n'); - for (az = al12; n_alpha--; ) { - al12 = az = adjlon(az + del_alpha); - geod_pre(); - geod_for(); - printLL(phi2, lam2); putchar('\n'); - } -} - static void /* generate intermediate geodesic coordinates */ -do_geod(void) { - double phil, laml, del_S; - - phil = phi2; - laml = lam2; - printLL(phi1, lam1); putchar('\n'); - for ( geod_S = del_S = geod_S / n_S; --n_S; geod_S += del_S) { - geod_for(); - printLL(phi2, lam2); putchar('\n'); - } - printLL(phil, laml); putchar('\n'); -} - static void /* file processing function */ -process(FILE *fid) { - char line[MAXLINE+3], *s; - - for (;;) { - ++emess_dat.File_line; - if (!(s = fgets(line, MAXLINE, fid))) - break; - if (!strchr(s, '\n')) { /* overlong line */ - int c; - strcat(s, "\n"); - /* gobble up to newline */ - while ((c = fgetc(fid)) != EOF && c != '\n') ; - } - if (*s == tag) { - fputs(line, stdout); - continue; - } - phi1 = dmstor(s, &s); - lam1 = dmstor(s, &s); - if (inverse) { - phi2 = dmstor(s, &s); - lam2 = dmstor(s, &s); - geod_inv(); - } else { - al12 = dmstor(s, &s); - geod_S = strtod(s, &s) * to_meter; - geod_pre(); - geod_for(); - } - if (!*s && (s > line)) --s; /* assumed we gobbled \n */ - if (pos_azi) { - if (al12 < 0.) al12 += M_TWOPI; - if (al21 < 0.) al21 += M_TWOPI; - } - if (fullout) { - printLL(phi1, lam1); TAB; - printLL(phi2, lam2); TAB; - if (oform) { - (void)printf(oform, al12 * RAD_TO_DEG); TAB; - (void)printf(oform, al21 * RAD_TO_DEG); TAB; - (void)printf(osform, geod_S * fr_meter); - } else { - (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; - (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; - (void)printf(osform, geod_S * fr_meter); - } - } else if (inverse) - if (oform) { - (void)printf(oform, al12 * RAD_TO_DEG); TAB; - (void)printf(oform, al21 * RAD_TO_DEG); TAB; - (void)printf(osform, geod_S * fr_meter); - } else { - (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; - (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; - (void)printf(osform, geod_S * fr_meter); - } - else { - printLL(phi2, lam2); TAB; - if (oform) - (void)printf(oform, al21 * RAD_TO_DEG); - else - (void)fputs(rtodms(pline, al21, 0, 0), stdout); - } - (void)fputs(s, stdout); - } -} - -static char *pargv[MAX_PARGS]; -static int pargc = 0; - -int main(int argc, char **argv) { - char *arg, **eargv = argv; - FILE *fid; - static int eargc = 0, c; - - if ((emess_dat.Prog_name = strrchr(*argv,'/')) != NULL) ++emess_dat.Prog_name; - else emess_dat.Prog_name = *argv; - inverse = ! strncmp(emess_dat.Prog_name, "inv", 3); - if (argc <= 1 ) { - (void)fprintf(stderr, usage, pj_get_release(), - emess_dat.Prog_name); - exit (0); - } - /* process run line arguments */ - while (--argc > 0) { /* collect run line arguments */ - if(**++argv == '-') for(arg = *argv;;) { - switch(*++arg) { - case '\0': /* position of "stdin" */ - if (arg[-1] == '-') eargv[eargc++] = "-"; - break; - case 'a': /* output full set of values */ - fullout = 1; - continue; - case 'I': /* alt. inverse spec. */ - inverse = 1; - continue; - case 't': /* set col. one char */ - if (arg[1]) tag = *++arg; - else emess(1,"missing -t col. 1 tag"); - continue; - case 'W': /* specify seconds precision */ - case 'w': /* -W for constant field width */ - if ((c = arg[1]) && isdigit(c)) { - set_rtodms(c - '0', *arg == 'W'); - ++arg; - } else - emess(1,"-W argument missing or non-digit"); - continue; - case 'f': /* alternate output format degrees or xy */ - if (--argc <= 0) -noargument: emess(1,"missing argument for -%c",*arg); - oform = *++argv; - continue; - case 'F': /* alternate output format degrees or xy */ - if (--argc <= 0) goto noargument; - osform = *++argv; - continue; - case 'l': - if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ - const struct PJ_ELLPS *le; - - for (le=proj_list_ellps(); le->id ; ++le) - (void)printf("%9s %-16s %-16s %s\n", - le->id, le->major, le->ell, le->name); - } else if (arg[1] == 'u') { /* list of units */ - const struct PJ_UNITS *lu; - - for (lu = proj_list_units();lu->id ; ++lu) - (void)printf("%12s %-20s %s\n", - lu->id, lu->to_meter, lu->name); - } else - emess(1,"invalid list option: l%c",arg[1]); - exit( 0 ); - case 'p': /* output azimuths as positive */ - pos_azi = 1; - continue; - default: - emess(1, "invalid option: -%c",*arg); - break; - } - break; - } else if (**argv == '+') /* + argument */ - if (pargc < MAX_PARGS) - pargv[pargc++] = *argv + 1; - else - emess(1,"overflowed + argument table"); - else /* assumed to be input file name(s) */ - eargv[eargc++] = *argv; - } - /* done with parameter and control input */ - geod_set(pargc, pargv); /* setup projection */ - if ((n_alpha || n_S) && eargc) - emess(1,"files specified for arc/geodesic mode"); - if (n_alpha) - do_arc(); - else if (n_S) - do_geod(); - else { /* process input file list */ - if (eargc == 0) /* if no specific files force sysin */ - eargv[eargc++] = "-"; - for ( ; eargc-- ; ++eargv) { - if (**eargv == '-') { - fid = stdin; - emess_dat.File_name = ""; - } else { - if ((fid = fopen(*eargv, "r")) == NULL) { - emess(-2, *eargv, "input file"); - continue; - } - emess_dat.File_name = *eargv; - } - emess_dat.File_line = 0; - process(fid); - (void)fclose(fid); - emess_dat.File_name = (char *)0; - } - } - exit(0); /* normal completion */ -} diff --git a/src/geod.cpp b/src/geod.cpp new file mode 100644 index 00000000..bb52818e --- /dev/null +++ b/src/geod.cpp @@ -0,0 +1,239 @@ +/* <<<< Geodesic filter program >>>> */ + +#include "proj.h" +# include "projects.h" +# include "geod_interface.h" +# include "emess.h" +# include +# include +# include + +# define MAXLINE 200 +# define MAX_PARGS 50 +# define TAB putchar('\t') + static int +fullout = 0, /* output full set of geodesic values */ +tag = '#', /* beginning of line tag character */ +pos_azi = 0, /* output azimuths as positive values */ +inverse = 0; /* != 0 then inverse geodesic */ + static char +*oform = (char *)0, /* output format for decimal degrees */ +*osform = "%.3f", /* output format for S */ +pline[50], /* work string */ +*usage = +"%s\nusage: %s [ -afFIlptwW [args] ] [ +opts[=arg] ] [ files ]\n"; + static void +printLL(double p, double l) { + if (oform) { + (void)printf(oform, p * RAD_TO_DEG); TAB; + (void)printf(oform, l * RAD_TO_DEG); + } else { + (void)fputs(rtodms(pline, p, 'N', 'S'),stdout); TAB; + (void)fputs(rtodms(pline, l, 'E', 'W'),stdout); + } +} + static void +do_arc(void) { + double az; + + printLL(phi2, lam2); putchar('\n'); + for (az = al12; n_alpha--; ) { + al12 = az = adjlon(az + del_alpha); + geod_pre(); + geod_for(); + printLL(phi2, lam2); putchar('\n'); + } +} + static void /* generate intermediate geodesic coordinates */ +do_geod(void) { + double phil, laml, del_S; + + phil = phi2; + laml = lam2; + printLL(phi1, lam1); putchar('\n'); + for ( geod_S = del_S = geod_S / n_S; --n_S; geod_S += del_S) { + geod_for(); + printLL(phi2, lam2); putchar('\n'); + } + printLL(phil, laml); putchar('\n'); +} + static void /* file processing function */ +process(FILE *fid) { + char line[MAXLINE+3], *s; + + for (;;) { + ++emess_dat.File_line; + if (!(s = fgets(line, MAXLINE, fid))) + break; + if (!strchr(s, '\n')) { /* overlong line */ + int c; + strcat(s, "\n"); + /* gobble up to newline */ + while ((c = fgetc(fid)) != EOF && c != '\n') ; + } + if (*s == tag) { + fputs(line, stdout); + continue; + } + phi1 = dmstor(s, &s); + lam1 = dmstor(s, &s); + if (inverse) { + phi2 = dmstor(s, &s); + lam2 = dmstor(s, &s); + geod_inv(); + } else { + al12 = dmstor(s, &s); + geod_S = strtod(s, &s) * to_meter; + geod_pre(); + geod_for(); + } + if (!*s && (s > line)) --s; /* assumed we gobbled \n */ + if (pos_azi) { + if (al12 < 0.) al12 += M_TWOPI; + if (al21 < 0.) al21 += M_TWOPI; + } + if (fullout) { + printLL(phi1, lam1); TAB; + printLL(phi2, lam2); TAB; + if (oform) { + (void)printf(oform, al12 * RAD_TO_DEG); TAB; + (void)printf(oform, al21 * RAD_TO_DEG); TAB; + (void)printf(osform, geod_S * fr_meter); + } else { + (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; + (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; + (void)printf(osform, geod_S * fr_meter); + } + } else if (inverse) + if (oform) { + (void)printf(oform, al12 * RAD_TO_DEG); TAB; + (void)printf(oform, al21 * RAD_TO_DEG); TAB; + (void)printf(osform, geod_S * fr_meter); + } else { + (void)fputs(rtodms(pline, al12, 0, 0), stdout); TAB; + (void)fputs(rtodms(pline, al21, 0, 0), stdout); TAB; + (void)printf(osform, geod_S * fr_meter); + } + else { + printLL(phi2, lam2); TAB; + if (oform) + (void)printf(oform, al21 * RAD_TO_DEG); + else + (void)fputs(rtodms(pline, al21, 0, 0), stdout); + } + (void)fputs(s, stdout); + } +} + +static char *pargv[MAX_PARGS]; +static int pargc = 0; + +int main(int argc, char **argv) { + char *arg, **eargv = argv; + FILE *fid; + static int eargc = 0, c; + + if ((emess_dat.Prog_name = strrchr(*argv,'/')) != NULL) ++emess_dat.Prog_name; + else emess_dat.Prog_name = *argv; + inverse = ! strncmp(emess_dat.Prog_name, "inv", 3); + if (argc <= 1 ) { + (void)fprintf(stderr, usage, pj_get_release(), + emess_dat.Prog_name); + exit (0); + } + /* process run line arguments */ + while (--argc > 0) { /* collect run line arguments */ + if(**++argv == '-') for(arg = *argv;;) { + switch(*++arg) { + case '\0': /* position of "stdin" */ + if (arg[-1] == '-') eargv[eargc++] = "-"; + break; + case 'a': /* output full set of values */ + fullout = 1; + continue; + case 'I': /* alt. inverse spec. */ + inverse = 1; + continue; + case 't': /* set col. one char */ + if (arg[1]) tag = *++arg; + else emess(1,"missing -t col. 1 tag"); + continue; + case 'W': /* specify seconds precision */ + case 'w': /* -W for constant field width */ + if ((c = arg[1]) && isdigit(c)) { + set_rtodms(c - '0', *arg == 'W'); + ++arg; + } else + emess(1,"-W argument missing or non-digit"); + continue; + case 'f': /* alternate output format degrees or xy */ + if (--argc <= 0) +noargument: emess(1,"missing argument for -%c",*arg); + oform = *++argv; + continue; + case 'F': /* alternate output format degrees or xy */ + if (--argc <= 0) goto noargument; + osform = *++argv; + continue; + case 'l': + if (!arg[1] || arg[1] == 'e') { /* list of ellipsoids */ + const struct PJ_ELLPS *le; + + for (le=proj_list_ellps(); le->id ; ++le) + (void)printf("%9s %-16s %-16s %s\n", + le->id, le->major, le->ell, le->name); + } else if (arg[1] == 'u') { /* list of units */ + const struct PJ_UNITS *lu; + + for (lu = proj_list_units();lu->id ; ++lu) + (void)printf("%12s %-20s %s\n", + lu->id, lu->to_meter, lu->name); + } else + emess(1,"invalid list option: l%c",arg[1]); + exit( 0 ); + case 'p': /* output azimuths as positive */ + pos_azi = 1; + continue; + default: + emess(1, "invalid option: -%c",*arg); + break; + } + break; + } else if (**argv == '+') /* + argument */ + if (pargc < MAX_PARGS) + pargv[pargc++] = *argv + 1; + else + emess(1,"overflowed + argument table"); + else /* assumed to be input file name(s) */ + eargv[eargc++] = *argv; + } + /* done with parameter and control input */ + geod_set(pargc, pargv); /* setup projection */ + if ((n_alpha || n_S) && eargc) + emess(1,"files specified for arc/geodesic mode"); + if (n_alpha) + do_arc(); + else if (n_S) + do_geod(); + else { /* process input file list */ + if (eargc == 0) /* if no specific files force sysin */ + eargv[eargc++] = "-"; + for ( ; eargc-- ; ++eargv) { + if (**eargv == '-') { + fid = stdin; + emess_dat.File_name = ""; + } else { + if ((fid = fopen(*eargv, "r")) == NULL) { + emess(-2, *eargv, "input file"); + continue; + } + emess_dat.File_name = *eargv; + } + emess_dat.File_line = 0; + process(fid); + (void)fclose(fid); + emess_dat.File_name = (char *)0; + } + } + exit(0); /* normal completion */ +} diff --git a/src/geod_interface.c b/src/geod_interface.c deleted file mode 100644 index a30377ac..00000000 --- a/src/geod_interface.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "projects.h" -#include "geod_interface.h" - -void geod_ini(void) { - geod_init(&GlobalGeodesic, geod_a, geod_f); -} - -void geod_pre(void) { - double - lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, - azi1 = al12 / DEG_TO_RAD; - geod_lineinit(&GlobalGeodesicLine, &GlobalGeodesic, lat1, lon1, azi1, 0U); -} - -void geod_for(void) { - double - s12 = geod_S, lat2, lon2, azi2; - geod_position(&GlobalGeodesicLine, s12, &lat2, &lon2, &azi2); - azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ - phi2 = lat2 * DEG_TO_RAD; - lam2 = lon2 * DEG_TO_RAD; - al21 = azi2 * DEG_TO_RAD; -} - -void geod_inv(void) { - double - lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, - lat2 = phi2 / DEG_TO_RAD, lon2 = lam2 / DEG_TO_RAD, - azi1, azi2, s12; - geod_inverse(&GlobalGeodesic, lat1, lon1, lat2, lon2, &s12, &azi1, &azi2); - azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ - al12 = azi1 * DEG_TO_RAD; al21 = azi2 * DEG_TO_RAD; geod_S = s12; -} diff --git a/src/geod_interface.cpp b/src/geod_interface.cpp new file mode 100644 index 00000000..a30377ac --- /dev/null +++ b/src/geod_interface.cpp @@ -0,0 +1,33 @@ +#include "projects.h" +#include "geod_interface.h" + +void geod_ini(void) { + geod_init(&GlobalGeodesic, geod_a, geod_f); +} + +void geod_pre(void) { + double + lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, + azi1 = al12 / DEG_TO_RAD; + geod_lineinit(&GlobalGeodesicLine, &GlobalGeodesic, lat1, lon1, azi1, 0U); +} + +void geod_for(void) { + double + s12 = geod_S, lat2, lon2, azi2; + geod_position(&GlobalGeodesicLine, s12, &lat2, &lon2, &azi2); + azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ + phi2 = lat2 * DEG_TO_RAD; + lam2 = lon2 * DEG_TO_RAD; + al21 = azi2 * DEG_TO_RAD; +} + +void geod_inv(void) { + double + lat1 = phi1 / DEG_TO_RAD, lon1 = lam1 / DEG_TO_RAD, + lat2 = phi2 / DEG_TO_RAD, lon2 = lam2 / DEG_TO_RAD, + azi1, azi2, s12; + geod_inverse(&GlobalGeodesic, lat1, lon1, lat2, lon2, &s12, &azi1, &azi2); + azi2 += azi2 >= 0 ? -180 : 180; /* Compute back azimuth */ + al12 = azi1 * DEG_TO_RAD; al21 = azi2 * DEG_TO_RAD; geod_S = s12; +} diff --git a/src/geod_set.c b/src/geod_set.c deleted file mode 100644 index b5bd0667..00000000 --- a/src/geod_set.c +++ /dev/null @@ -1,75 +0,0 @@ -#define _IN_GEOD_SET - -#include -#include -#include - -#include "proj.h" -#include "projects.h" -#include "geod_interface.h" -#include "emess.h" - - void -geod_set(int argc, char **argv) { - paralist *start = 0, *curr; - double es; - char *name; - int i; - - /* put arguments into internal linked list */ - if (argc <= 0) - emess(1, "no arguments in initialization list"); - start = curr = pj_mkparam(argv[0]); - if (!curr) - emess(1, "memory allocation failed"); - for (i = 1; curr != 0 && i < argc; ++i) { - curr->next = pj_mkparam(argv[i]); - if (!curr->next) - emess(1, "memory allocation failed"); - curr = curr->next; - } - /* set elliptical parameters */ - if (pj_ell_set(pj_get_default_ctx(),start, &geod_a, &es)) emess(1,"ellipse setup failure"); - /* set units */ - if ((name = pj_param(NULL,start, "sunits").s) != NULL) { - const char *s; - const struct PJ_UNITS *unit_list = proj_list_units(); - for (i = 0; (s = unit_list[i].id) && strcmp(name, s) ; ++i) ; - if (!s) - emess(1,"%s unknown unit conversion id", name); - to_meter = unit_list[i].factor; - fr_meter = 1 / to_meter; - } else - to_meter = fr_meter = 1; - geod_f = es/(1 + sqrt(1 - es)); - geod_ini(); - /* check if line or arc mode */ - if (pj_param(NULL,start, "tlat_1").i) { - double del_S; -#undef f - phi1 = pj_param(NULL,start, "rlat_1").f; - lam1 = pj_param(NULL,start, "rlon_1").f; - if (pj_param(NULL,start, "tlat_2").i) { - phi2 = pj_param(NULL,start, "rlat_2").f; - lam2 = pj_param(NULL,start, "rlon_2").f; - geod_inv(); - geod_pre(); - } else if ((geod_S = pj_param(NULL,start, "dS").f) != 0.) { - al12 = pj_param(NULL,start, "rA").f; - geod_pre(); - geod_for(); - } else emess(1,"incomplete geodesic/arc info"); - if ((n_alpha = pj_param(NULL,start, "in_A").i) > 0) { - if ((del_alpha = pj_param(NULL,start, "rdel_A").f) == 0.0) - emess(1,"del azimuth == 0"); - } else if ((del_S = fabs(pj_param(NULL,start, "ddel_S").f)) != 0.) { - n_S = (int)(geod_S / del_S + .5); - } else if ((n_S = pj_param(NULL,start, "in_S").i) <= 0) - emess(1,"no interval divisor selected"); - } - /* free up linked list */ - for ( ; start; start = curr) { - curr = start->next; - pj_dalloc(start); - } -} diff --git a/src/geod_set.cpp b/src/geod_set.cpp new file mode 100644 index 00000000..b5bd0667 --- /dev/null +++ b/src/geod_set.cpp @@ -0,0 +1,75 @@ +#define _IN_GEOD_SET + +#include +#include +#include + +#include "proj.h" +#include "projects.h" +#include "geod_interface.h" +#include "emess.h" + + void +geod_set(int argc, char **argv) { + paralist *start = 0, *curr; + double es; + char *name; + int i; + + /* put arguments into internal linked list */ + if (argc <= 0) + emess(1, "no arguments in initialization list"); + start = curr = pj_mkparam(argv[0]); + if (!curr) + emess(1, "memory allocation failed"); + for (i = 1; curr != 0 && i < argc; ++i) { + curr->next = pj_mkparam(argv[i]); + if (!curr->next) + emess(1, "memory allocation failed"); + curr = curr->next; + } + /* set elliptical parameters */ + if (pj_ell_set(pj_get_default_ctx(),start, &geod_a, &es)) emess(1,"ellipse setup failure"); + /* set units */ + if ((name = pj_param(NULL,start, "sunits").s) != NULL) { + const char *s; + const struct PJ_UNITS *unit_list = proj_list_units(); + for (i = 0; (s = unit_list[i].id) && strcmp(name, s) ; ++i) ; + if (!s) + emess(1,"%s unknown unit conversion id", name); + to_meter = unit_list[i].factor; + fr_meter = 1 / to_meter; + } else + to_meter = fr_meter = 1; + geod_f = es/(1 + sqrt(1 - es)); + geod_ini(); + /* check if line or arc mode */ + if (pj_param(NULL,start, "tlat_1").i) { + double del_S; +#undef f + phi1 = pj_param(NULL,start, "rlat_1").f; + lam1 = pj_param(NULL,start, "rlon_1").f; + if (pj_param(NULL,start, "tlat_2").i) { + phi2 = pj_param(NULL,start, "rlat_2").f; + lam2 = pj_param(NULL,start, "rlon_2").f; + geod_inv(); + geod_pre(); + } else if ((geod_S = pj_param(NULL,start, "dS").f) != 0.) { + al12 = pj_param(NULL,start, "rA").f; + geod_pre(); + geod_for(); + } else emess(1,"incomplete geodesic/arc info"); + if ((n_alpha = pj_param(NULL,start, "in_A").i) > 0) { + if ((del_alpha = pj_param(NULL,start, "rdel_A").f) == 0.0) + emess(1,"del azimuth == 0"); + } else if ((del_S = fabs(pj_param(NULL,start, "ddel_S").f)) != 0.) { + n_S = (int)(geod_S / del_S + .5); + } else if ((n_S = pj_param(NULL,start, "in_S").i) <= 0) + emess(1,"no interval divisor selected"); + } + /* free up linked list */ + for ( ; start; start = curr) { + curr = start->next; + pj_dalloc(start); + } +} diff --git a/src/geodesic.c b/src/geodesic.c deleted file mode 100644 index 220dcd7f..00000000 --- a/src/geodesic.c +++ /dev/null @@ -1,2100 +0,0 @@ -/** - * \file geodesic.c - * \brief Implementation of the geodesic routines in C - * - * For the full documentation see geodesic.h. - **********************************************************************/ - -/** @cond SKIP */ - -/* - * This is a C implementation of the geodesic algorithms described in - * - * C. F. F. Karney, - * Algorithms for geodesics, - * J. Geodesy 87, 43--55 (2013); - * https://doi.org/10.1007/s00190-012-0578-z - * Addenda: https://geographiclib.sourceforge.io/geod-addenda.html - * - * See the comments in geodesic.h for documentation. - * - * Copyright (c) Charles Karney (2012-2018) and licensed - * under the MIT/X11 License. For more information, see - * https://geographiclib.sourceforge.io/ - */ - -#include "geodesic.h" -#ifdef PJ_LIB__ -#include "proj_math.h" -#else -#include -#endif - -#if !defined(HAVE_C99_MATH) -#define HAVE_C99_MATH 0 -#endif - -#define GEOGRAPHICLIB_GEODESIC_ORDER 6 -#define nA1 GEOGRAPHICLIB_GEODESIC_ORDER -#define nC1 GEOGRAPHICLIB_GEODESIC_ORDER -#define nC1p GEOGRAPHICLIB_GEODESIC_ORDER -#define nA2 GEOGRAPHICLIB_GEODESIC_ORDER -#define nC2 GEOGRAPHICLIB_GEODESIC_ORDER -#define nA3 GEOGRAPHICLIB_GEODESIC_ORDER -#define nA3x nA3 -#define nC3 GEOGRAPHICLIB_GEODESIC_ORDER -#define nC3x ((nC3 * (nC3 - 1)) / 2) -#define nC4 GEOGRAPHICLIB_GEODESIC_ORDER -#define nC4x ((nC4 * (nC4 + 1)) / 2) -#define nC (GEOGRAPHICLIB_GEODESIC_ORDER + 1) - -typedef double real; -typedef int boolx; - -static unsigned init = 0; -static const int FALSE = 0; -static const int TRUE = 1; -static unsigned digits, maxit1, maxit2; -static real epsilon, realmin, pi, degree, NaN, - tiny, tol0, tol1, tol2, tolb, xthresh; - -static void Init() { - if (!init) { -#if defined(__DBL_MANT_DIG__) - digits = __DBL_MANT_DIG__; -#else - digits = 53; -#endif -#if defined(__DBL_EPSILON__) - epsilon = __DBL_EPSILON__; -#else - epsilon = pow(0.5, digits - 1); -#endif -#if defined(__DBL_MIN__) - realmin = __DBL_MIN__; -#else - realmin = pow(0.5, 1022); -#endif -#if defined(M_PI) - pi = M_PI; -#else - pi = atan2(0.0, -1.0); -#endif - maxit1 = 20; - maxit2 = maxit1 + digits + 10; - tiny = sqrt(realmin); - tol0 = epsilon; - /* Increase multiplier in defn of tol1 from 100 to 200 to fix inverse case - * 52.784459512564 0 -52.784459512563990912 179.634407464943777557 - * which otherwise failed for Visual Studio 10 (Release and Debug) */ - tol1 = 200 * tol0; - tol2 = sqrt(tol0); - /* Check on bisection interval */ - tolb = tol0 * tol2; - xthresh = 1000 * tol2; - degree = pi/180; - #if defined(NAN) - NaN = NAN; - #else - { - real minus1 = -1; - /* cppcheck-suppress wrongmathcall */ - NaN = sqrt(minus1); - } - #endif - init = 1; - } -} - -enum captype { - CAP_NONE = 0U, - CAP_C1 = 1U<<0, - CAP_C1p = 1U<<1, - CAP_C2 = 1U<<2, - CAP_C3 = 1U<<3, - CAP_C4 = 1U<<4, - CAP_ALL = 0x1FU, - OUT_ALL = 0x7F80U -}; - -static real sq(real x) { return x * x; } -#if HAVE_C99_MATH -#define atanhx atanh -#define copysignx copysign -#define hypotx hypot -#define cbrtx cbrt -#else -static real log1px(real x) { - volatile real - y = 1 + x, - z = y - 1; - /* Here's the explanation for this magic: y = 1 + z, exactly, and z - * approx x, thus log(y)/z (which is nearly constant near z = 0) returns - * a good approximation to the true log(1 + x)/x. The multiplication x * - * (log(y)/z) introduces little additional error. */ - return z == 0 ? x : x * log(y) / z; -} - -static real atanhx(real x) { - real y = fabs(x); /* Enforce odd parity */ - y = log1px(2 * y/(1 - y))/2; - return x < 0 ? -y : y; -} - -static real copysignx(real x, real y) { - return fabs(x) * (y < 0 || (y == 0 && 1/y < 0) ? -1 : 1); -} - -static real hypotx(real x, real y) -{ return sqrt(x * x + y * y); } - -static real cbrtx(real x) { - real y = pow(fabs(x), 1/(real)(3)); /* Return the real cube root */ - return x < 0 ? -y : y; -} -#endif - -static real sumx(real u, real v, real* t) { - volatile real s = u + v; - volatile real up = s - v; - volatile real vpp = s - up; - up -= u; - vpp -= v; - if (t) *t = -(up + vpp); - /* error-free sum: - * u + v = s + t - * = round(u + v) + t */ - return s; -} - -static real polyval(int N, const real p[], real x) { - real y = N < 0 ? 0 : *p++; - while (--N >= 0) y = y * x + *p++; - return y; -} - -/* mimic C++ std::min and std::max */ -static real minx(real a, real b) -{ return (b < a) ? b : a; } - -static real maxx(real a, real b) -{ return (a < b) ? b : a; } - -static void swapx(real* x, real* y) -{ real t = *x; *x = *y; *y = t; } - -static void norm2(real* sinx, real* cosx) { - real r = hypotx(*sinx, *cosx); - *sinx /= r; - *cosx /= r; -} - -static real AngNormalize(real x) { -#if HAVE_C99_MATH - x = remainder(x, (real)(360)); - return x != -180 ? x : 180; -#else - real y = fmod(x, (real)(360)); -#if defined(_MSC_VER) && _MSC_VER < 1900 - /* - * Before version 14 (2015), Visual Studio had problems dealing - * with -0.0. Specifically - * VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 - * sincosdx has a similar fix. - * python 2.7 on Windows 32-bit machines has the same problem. - */ - if (x == 0) y = x; -#endif - return y <= -180 ? y + 360 : (y <= 180 ? y : y - 360); -#endif -} - -static real LatFix(real x) -{ return fabs(x) > 90 ? NaN : x; } - -static real AngDiff(real x, real y, real* e) { - real t, d = AngNormalize(sumx(AngNormalize(-x), AngNormalize(y), &t)); - /* Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and - * abs(t) <= eps (eps = 2^-45 for doubles). The only case where the - * addition of t takes the result outside the range (-180,180] is d = 180 - * and t > 0. The case, d = -180 + eps, t = -eps, can't happen, since - * sum would have returned the exact result in such a case (i.e., given t - * = 0). */ - return sumx(d == 180 && t > 0 ? -180 : d, t, e); -} - -static real AngRound(real x) { - const real z = 1/(real)(16); - volatile real y; - if (x == 0) return 0; - y = fabs(x); - /* The compiler mustn't "simplify" z - (z - y) to y */ - y = y < z ? z - (z - y) : y; - return x < 0 ? -y : y; -} - -static void sincosdx(real x, real* sinx, real* cosx) { - /* In order to minimize round-off errors, this function exactly reduces - * the argument to the range [-45, 45] before converting it to radians. */ - real r, s, c; int q; -#if HAVE_C99_MATH && !defined(__GNUC__) - /* Disable for gcc because of bug in glibc version < 2.22, see - * https://sourceware.org/bugzilla/show_bug.cgi?id=17569 */ - r = remquo(x, (real)(90), &q); -#else - r = fmod(x, (real)(360)); - /* check for NaN */ - q = r == r ? (int)(floor(r / 90 + (real)(0.5))) : 0; - r -= 90 * q; -#endif - /* now abs(r) <= 45 */ - r *= degree; - /* Possibly could call the gnu extension sincos */ - s = sin(r); c = cos(r); -#if defined(_MSC_VER) && _MSC_VER < 1900 - /* - * Before version 14 (2015), Visual Studio had problems dealing - * with -0.0. Specifically - * VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 - * VC 12 and 64-bit compile: sin(-0.0) -> +0.0 - * AngNormalize has a similar fix. - * python 2.7 on Windows 32-bit machines has the same problem. - */ - if (x == 0) s = x; -#endif - switch ((unsigned)q & 3U) { - case 0U: *sinx = s; *cosx = c; break; - case 1U: *sinx = c; *cosx = -s; break; - case 2U: *sinx = -s; *cosx = -c; break; - default: *sinx = -c; *cosx = s; break; /* case 3U */ - } - if (x != 0) { *sinx += (real)(0); *cosx += (real)(0); } -} - -static real atan2dx(real y, real x) { - /* In order to minimize round-off errors, this function rearranges the - * arguments so that result of atan2 is in the range [-pi/4, pi/4] before - * converting it to degrees and mapping the result to the correct - * quadrant. */ - int q = 0; real ang; - if (fabs(y) > fabs(x)) { swapx(&x, &y); q = 2; } - if (x < 0) { x = -x; ++q; } - /* here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4] */ - ang = atan2(y, x) / degree; - switch (q) { - /* Note that atan2d(-0.0, 1.0) will return -0. However, we expect that - * atan2d will not be called with y = -0. If need be, include - * - * case 0: ang = 0 + ang; break; - */ - case 1: ang = (y >= 0 ? 180 : -180) - ang; break; - case 2: ang = 90 - ang; break; - case 3: ang = -90 + ang; break; - } - return ang; -} - -static void A3coeff(struct geod_geodesic* g); -static void C3coeff(struct geod_geodesic* g); -static void C4coeff(struct geod_geodesic* g); -static real SinCosSeries(boolx sinp, - real sinx, real cosx, - const real c[], int n); -static void Lengths(const struct geod_geodesic* g, - real eps, real sig12, - real ssig1, real csig1, real dn1, - real ssig2, real csig2, real dn2, - real cbet1, real cbet2, - real* ps12b, real* pm12b, real* pm0, - real* pM12, real* pM21, - /* Scratch area of the right size */ - real Ca[]); -static real Astroid(real x, real y); -static real InverseStart(const struct geod_geodesic* g, - real sbet1, real cbet1, real dn1, - real sbet2, real cbet2, real dn2, - real lam12, real slam12, real clam12, - real* psalp1, real* pcalp1, - /* Only updated if return val >= 0 */ - real* psalp2, real* pcalp2, - /* Only updated for short lines */ - real* pdnm, - /* Scratch area of the right size */ - real Ca[]); -static real Lambda12(const struct geod_geodesic* g, - real sbet1, real cbet1, real dn1, - real sbet2, real cbet2, real dn2, - real salp1, real calp1, - real slam120, real clam120, - real* psalp2, real* pcalp2, - real* psig12, - real* pssig1, real* pcsig1, - real* pssig2, real* pcsig2, - real* peps, - real* pdomg12, - boolx diffp, real* pdlam12, - /* Scratch area of the right size */ - real Ca[]); -static real A3f(const struct geod_geodesic* g, real eps); -static void C3f(const struct geod_geodesic* g, real eps, real c[]); -static void C4f(const struct geod_geodesic* g, real eps, real c[]); -static real A1m1f(real eps); -static void C1f(real eps, real c[]); -static void C1pf(real eps, real c[]); -static real A2m1f(real eps); -static void C2f(real eps, real c[]); -static int transit(real lon1, real lon2); -static int transitdirect(real lon1, real lon2); -static void accini(real s[]); -static void acccopy(const real s[], real t[]); -static void accadd(real s[], real y); -static real accsum(const real s[], real y); -static void accneg(real s[]); - -void geod_init(struct geod_geodesic* g, real a, real f) { - if (!init) Init(); - g->a = a; - g->f = f; - g->f1 = 1 - g->f; - g->e2 = g->f * (2 - g->f); - g->ep2 = g->e2 / sq(g->f1); /* e2 / (1 - e2) */ - g->n = g->f / ( 2 - g->f); - g->b = g->a * g->f1; - g->c2 = (sq(g->a) + sq(g->b) * - (g->e2 == 0 ? 1 : - (g->e2 > 0 ? atanhx(sqrt(g->e2)) : atan(sqrt(-g->e2))) / - sqrt(fabs(g->e2))))/2; /* authalic radius squared */ - /* The sig12 threshold for "really short". Using the auxiliary sphere - * solution with dnm computed at (bet1 + bet2) / 2, the relative error in the - * azimuth consistency check is sig12^2 * abs(f) * min(1, 1-f/2) / 2. (Error - * measured for 1/100 < b/a < 100 and abs(f) >= 1/1000. For a given f and - * sig12, the max error occurs for lines near the pole. If the old rule for - * computing dnm = (dn1 + dn2)/2 is used, then the error increases by a - * factor of 2.) Setting this equal to epsilon gives sig12 = etol2. Here - * 0.1 is a safety factor (error decreased by 100) and max(0.001, abs(f)) - * stops etol2 getting too large in the nearly spherical case. */ - g->etol2 = 0.1 * tol2 / - sqrt( maxx((real)(0.001), fabs(g->f)) * minx((real)(1), 1 - g->f/2) / 2 ); - - A3coeff(g); - C3coeff(g); - C4coeff(g); -} - -static void geod_lineinit_int(struct geod_geodesicline* l, - const struct geod_geodesic* g, - real lat1, real lon1, - real azi1, real salp1, real calp1, - unsigned caps) { - real cbet1, sbet1, eps; - l->a = g->a; - l->f = g->f; - l->b = g->b; - l->c2 = g->c2; - l->f1 = g->f1; - /* If caps is 0 assume the standard direct calculation */ - l->caps = (caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE) | - /* always allow latitude and azimuth and unrolling of longitude */ - GEOD_LATITUDE | GEOD_AZIMUTH | GEOD_LONG_UNROLL; - - l->lat1 = LatFix(lat1); - l->lon1 = lon1; - l->azi1 = azi1; - l->salp1 = salp1; - l->calp1 = calp1; - - sincosdx(AngRound(l->lat1), &sbet1, &cbet1); sbet1 *= l->f1; - /* Ensure cbet1 = +epsilon at poles */ - norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1); - l->dn1 = sqrt(1 + g->ep2 * sq(sbet1)); - - /* Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), */ - l->salp0 = l->salp1 * cbet1; /* alp0 in [0, pi/2 - |bet1|] */ - /* Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following - * is slightly better (consider the case salp1 = 0). */ - l->calp0 = hypotx(l->calp1, l->salp1 * sbet1); - /* Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1). - * sig = 0 is nearest northward crossing of equator. - * With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line). - * With bet1 = pi/2, alp1 = -pi, sig1 = pi/2 - * With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2 - * Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1). - * With alp0 in (0, pi/2], quadrants for sig and omg coincide. - * No atan2(0,0) ambiguity at poles since cbet1 = +epsilon. - * With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi. */ - l->ssig1 = sbet1; l->somg1 = l->salp0 * sbet1; - l->csig1 = l->comg1 = sbet1 != 0 || l->calp1 != 0 ? cbet1 * l->calp1 : 1; - norm2(&l->ssig1, &l->csig1); /* sig1 in (-pi, pi] */ - /* norm2(somg1, comg1); -- don't need to normalize! */ - - l->k2 = sq(l->calp0) * g->ep2; - eps = l->k2 / (2 * (1 + sqrt(1 + l->k2)) + l->k2); - - if (l->caps & CAP_C1) { - real s, c; - l->A1m1 = A1m1f(eps); - C1f(eps, l->C1a); - l->B11 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C1a, nC1); - s = sin(l->B11); c = cos(l->B11); - /* tau1 = sig1 + B11 */ - l->stau1 = l->ssig1 * c + l->csig1 * s; - l->ctau1 = l->csig1 * c - l->ssig1 * s; - /* Not necessary because C1pa reverts C1a - * B11 = -SinCosSeries(TRUE, stau1, ctau1, C1pa, nC1p); */ - } - - if (l->caps & CAP_C1p) - C1pf(eps, l->C1pa); - - if (l->caps & CAP_C2) { - l->A2m1 = A2m1f(eps); - C2f(eps, l->C2a); - l->B21 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C2a, nC2); - } - - if (l->caps & CAP_C3) { - C3f(g, eps, l->C3a); - l->A3c = -l->f * l->salp0 * A3f(g, eps); - l->B31 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C3a, nC3-1); - } - - if (l->caps & CAP_C4) { - C4f(g, eps, l->C4a); - /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) */ - l->A4 = sq(l->a) * l->calp0 * l->salp0 * g->e2; - l->B41 = SinCosSeries(FALSE, l->ssig1, l->csig1, l->C4a, nC4); - } - - l->a13 = l->s13 = NaN; -} - -void geod_lineinit(struct geod_geodesicline* l, - const struct geod_geodesic* g, - real lat1, real lon1, real azi1, unsigned caps) { - real salp1, calp1; - azi1 = AngNormalize(azi1); - /* Guard against underflow in salp0 */ - sincosdx(AngRound(azi1), &salp1, &calp1); - geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); -} - -void geod_gendirectline(struct geod_geodesicline* l, - const struct geod_geodesic* g, - real lat1, real lon1, real azi1, - unsigned flags, real s12_a12, - unsigned caps) { - geod_lineinit(l, g, lat1, lon1, azi1, caps); - geod_gensetdistance(l, flags, s12_a12); -} - -void geod_directline(struct geod_geodesicline* l, - const struct geod_geodesic* g, - real lat1, real lon1, real azi1, - real s12, unsigned caps) { - geod_gendirectline(l, g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, caps); -} - -real geod_genposition(const struct geod_geodesicline* l, - unsigned flags, real s12_a12, - real* plat2, real* plon2, real* pazi2, - real* ps12, real* pm12, - real* pM12, real* pM21, - real* pS12) { - real lat2 = 0, lon2 = 0, azi2 = 0, s12 = 0, - m12 = 0, M12 = 0, M21 = 0, S12 = 0; - /* Avoid warning about uninitialized B12. */ - real sig12, ssig12, csig12, B12 = 0, AB1 = 0; - real omg12, lam12, lon12; - real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2; - unsigned outmask = - (plat2 ? GEOD_LATITUDE : 0U) | - (plon2 ? GEOD_LONGITUDE : 0U) | - (pazi2 ? GEOD_AZIMUTH : 0U) | - (ps12 ? GEOD_DISTANCE : 0U) | - (pm12 ? GEOD_REDUCEDLENGTH : 0U) | - (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | - (pS12 ? GEOD_AREA : 0U); - - outmask &= l->caps & OUT_ALL; - if (!( TRUE /*Init()*/ && - (flags & GEOD_ARCMODE || (l->caps & (GEOD_DISTANCE_IN & OUT_ALL))) )) - /* Uninitialized or impossible distance calculation requested */ - return NaN; - - if (flags & GEOD_ARCMODE) { - /* Interpret s12_a12 as spherical arc length */ - sig12 = s12_a12 * degree; - sincosdx(s12_a12, &ssig12, &csig12); - } else { - /* Interpret s12_a12 as distance */ - real - tau12 = s12_a12 / (l->b * (1 + l->A1m1)), - s = sin(tau12), - c = cos(tau12); - /* tau2 = tau1 + tau12 */ - B12 = - SinCosSeries(TRUE, - l->stau1 * c + l->ctau1 * s, - l->ctau1 * c - l->stau1 * s, - l->C1pa, nC1p); - sig12 = tau12 - (B12 - l->B11); - ssig12 = sin(sig12); csig12 = cos(sig12); - if (fabs(l->f) > 0.01) { - /* Reverted distance series is inaccurate for |f| > 1/100, so correct - * sig12 with 1 Newton iteration. The following table shows the - * approximate maximum error for a = WGS_a() and various f relative to - * GeodesicExact. - * erri = the error in the inverse solution (nm) - * errd = the error in the direct solution (series only) (nm) - * errda = the error in the direct solution (series + 1 Newton) (nm) - * - * f erri errd errda - * -1/5 12e6 1.2e9 69e6 - * -1/10 123e3 12e6 765e3 - * -1/20 1110 108e3 7155 - * -1/50 18.63 200.9 27.12 - * -1/100 18.63 23.78 23.37 - * -1/150 18.63 21.05 20.26 - * 1/150 22.35 24.73 25.83 - * 1/100 22.35 25.03 25.31 - * 1/50 29.80 231.9 30.44 - * 1/20 5376 146e3 10e3 - * 1/10 829e3 22e6 1.5e6 - * 1/5 157e6 3.8e9 280e6 */ - real serr; - ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; - csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; - B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); - serr = (1 + l->A1m1) * (sig12 + (B12 - l->B11)) - s12_a12 / l->b; - sig12 = sig12 - serr / sqrt(1 + l->k2 * sq(ssig2)); - ssig12 = sin(sig12); csig12 = cos(sig12); - /* Update B12 below */ - } - } - - /* sig2 = sig1 + sig12 */ - ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; - csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; - dn2 = sqrt(1 + l->k2 * sq(ssig2)); - if (outmask & (GEOD_DISTANCE | GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { - if (flags & GEOD_ARCMODE || fabs(l->f) > 0.01) - B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); - AB1 = (1 + l->A1m1) * (B12 - l->B11); - } - /* sin(bet2) = cos(alp0) * sin(sig2) */ - sbet2 = l->calp0 * ssig2; - /* Alt: cbet2 = hypot(csig2, salp0 * ssig2); */ - cbet2 = hypotx(l->salp0, l->calp0 * csig2); - if (cbet2 == 0) - /* I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case */ - cbet2 = csig2 = tiny; - /* tan(alp0) = cos(sig2)*tan(alp2) */ - salp2 = l->salp0; calp2 = l->calp0 * csig2; /* No need to normalize */ - - if (outmask & GEOD_DISTANCE) - s12 = (flags & GEOD_ARCMODE) ? - l->b * ((1 + l->A1m1) * sig12 + AB1) : - s12_a12; - - if (outmask & GEOD_LONGITUDE) { - real E = copysignx(1, l->salp0); /* east or west going? */ - /* tan(omg2) = sin(alp0) * tan(sig2) */ - somg2 = l->salp0 * ssig2; comg2 = csig2; /* No need to normalize */ - /* omg12 = omg2 - omg1 */ - omg12 = (flags & GEOD_LONG_UNROLL) - ? E * (sig12 - - (atan2( ssig2, csig2) - atan2( l->ssig1, l->csig1)) - + (atan2(E * somg2, comg2) - atan2(E * l->somg1, l->comg1))) - : atan2(somg2 * l->comg1 - comg2 * l->somg1, - comg2 * l->comg1 + somg2 * l->somg1); - lam12 = omg12 + l->A3c * - ( sig12 + (SinCosSeries(TRUE, ssig2, csig2, l->C3a, nC3-1) - - l->B31)); - lon12 = lam12 / degree; - lon2 = (flags & GEOD_LONG_UNROLL) ? l->lon1 + lon12 : - AngNormalize(AngNormalize(l->lon1) + AngNormalize(lon12)); - } - - if (outmask & GEOD_LATITUDE) - lat2 = atan2dx(sbet2, l->f1 * cbet2); - - if (outmask & GEOD_AZIMUTH) - azi2 = atan2dx(salp2, calp2); - - if (outmask & (GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { - real - B22 = SinCosSeries(TRUE, ssig2, csig2, l->C2a, nC2), - AB2 = (1 + l->A2m1) * (B22 - l->B21), - J12 = (l->A1m1 - l->A2m1) * sig12 + (AB1 - AB2); - if (outmask & GEOD_REDUCEDLENGTH) - /* Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure - * accurate cancellation in the case of coincident points. */ - m12 = l->b * ((dn2 * (l->csig1 * ssig2) - l->dn1 * (l->ssig1 * csig2)) - - l->csig1 * csig2 * J12); - if (outmask & GEOD_GEODESICSCALE) { - real t = l->k2 * (ssig2 - l->ssig1) * (ssig2 + l->ssig1) / - (l->dn1 + dn2); - M12 = csig12 + (t * ssig2 - csig2 * J12) * l->ssig1 / l->dn1; - M21 = csig12 - (t * l->ssig1 - l->csig1 * J12) * ssig2 / dn2; - } - } - - if (outmask & GEOD_AREA) { - real - B42 = SinCosSeries(FALSE, ssig2, csig2, l->C4a, nC4); - real salp12, calp12; - if (l->calp0 == 0 || l->salp0 == 0) { - /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ - salp12 = salp2 * l->calp1 - calp2 * l->salp1; - calp12 = calp2 * l->calp1 + salp2 * l->salp1; - } else { - /* tan(alp) = tan(alp0) * sec(sig) - * tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1) - * = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2) - * If csig12 > 0, write - * csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1) - * else - * csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1 - * No need to normalize */ - salp12 = l->calp0 * l->salp0 * - (csig12 <= 0 ? l->csig1 * (1 - csig12) + ssig12 * l->ssig1 : - ssig12 * (l->csig1 * ssig12 / (1 + csig12) + l->ssig1)); - calp12 = sq(l->salp0) + sq(l->calp0) * l->csig1 * csig2; - } - S12 = l->c2 * atan2(salp12, calp12) + l->A4 * (B42 - l->B41); - } - - /* In the pattern - * - * if ((outmask & GEOD_XX) && pYY) - * *pYY = YY; - * - * the second check "&& pYY" is redundant. It's there to make the CLang - * static analyzer happy. - */ - if ((outmask & GEOD_LATITUDE) && plat2) - *plat2 = lat2; - if ((outmask & GEOD_LONGITUDE) && plon2) - *plon2 = lon2; - if ((outmask & GEOD_AZIMUTH) && pazi2) - *pazi2 = azi2; - if ((outmask & GEOD_DISTANCE) && ps12) - *ps12 = s12; - if ((outmask & GEOD_REDUCEDLENGTH) && pm12) - *pm12 = m12; - if (outmask & GEOD_GEODESICSCALE) { - if (pM12) *pM12 = M12; - if (pM21) *pM21 = M21; - } - if ((outmask & GEOD_AREA) && pS12) - *pS12 = S12; - - return (flags & GEOD_ARCMODE) ? s12_a12 : sig12 / degree; -} - -void geod_setdistance(struct geod_geodesicline* l, real s13) { - l->s13 = s13; - l->a13 = geod_genposition(l, GEOD_NOFLAGS, l->s13, 0, 0, 0, 0, 0, 0, 0, 0); -} - -static void geod_setarc(struct geod_geodesicline* l, real a13) { - l->a13 = a13; l->s13 = NaN; - geod_genposition(l, GEOD_ARCMODE, l->a13, 0, 0, 0, &l->s13, 0, 0, 0, 0); -} - -void geod_gensetdistance(struct geod_geodesicline* l, - unsigned flags, real s13_a13) { - (flags & GEOD_ARCMODE) ? - geod_setarc(l, s13_a13) : - geod_setdistance(l, s13_a13); -} - -void geod_position(const struct geod_geodesicline* l, real s12, - real* plat2, real* plon2, real* pazi2) { - geod_genposition(l, FALSE, s12, plat2, plon2, pazi2, 0, 0, 0, 0, 0); -} - -real geod_gendirect(const struct geod_geodesic* g, - real lat1, real lon1, real azi1, - unsigned flags, real s12_a12, - real* plat2, real* plon2, real* pazi2, - real* ps12, real* pm12, real* pM12, real* pM21, - real* pS12) { - struct geod_geodesicline l; - unsigned outmask = - (plat2 ? GEOD_LATITUDE : 0U) | - (plon2 ? GEOD_LONGITUDE : 0U) | - (pazi2 ? GEOD_AZIMUTH : 0U) | - (ps12 ? GEOD_DISTANCE : 0U) | - (pm12 ? GEOD_REDUCEDLENGTH : 0U) | - (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | - (pS12 ? GEOD_AREA : 0U); - - geod_lineinit(&l, g, lat1, lon1, azi1, - /* Automatically supply GEOD_DISTANCE_IN if necessary */ - outmask | - ((flags & GEOD_ARCMODE) ? GEOD_NONE : GEOD_DISTANCE_IN)); - return geod_genposition(&l, flags, s12_a12, - plat2, plon2, pazi2, ps12, pm12, pM12, pM21, pS12); -} - -void geod_direct(const struct geod_geodesic* g, - real lat1, real lon1, real azi1, - real s12, - real* plat2, real* plon2, real* pazi2) { - geod_gendirect(g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, plat2, plon2, pazi2, - 0, 0, 0, 0, 0); -} - -static real geod_geninverse_int(const struct geod_geodesic* g, - real lat1, real lon1, real lat2, real lon2, - real* ps12, - real* psalp1, real* pcalp1, - real* psalp2, real* pcalp2, - real* pm12, real* pM12, real* pM21, - real* pS12) { - real s12 = 0, m12 = 0, M12 = 0, M21 = 0, S12 = 0; - real lon12, lon12s; - int latsign, lonsign, swapp; - real sbet1, cbet1, sbet2, cbet2, s12x = 0, m12x = 0; - real dn1, dn2, lam12, slam12, clam12; - real a12 = 0, sig12, calp1 = 0, salp1 = 0, calp2 = 0, salp2 = 0; - real Ca[nC]; - boolx meridian; - /* somg12 > 1 marks that it needs to be calculated */ - real omg12 = 0, somg12 = 2, comg12 = 0; - - unsigned outmask = - (ps12 ? GEOD_DISTANCE : 0U) | - (pm12 ? GEOD_REDUCEDLENGTH : 0U) | - (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | - (pS12 ? GEOD_AREA : 0U); - - outmask &= OUT_ALL; - /* Compute longitude difference (AngDiff does this carefully). Result is - * in [-180, 180] but -180 is only for west-going geodesics. 180 is for - * east-going and meridional geodesics. */ - lon12 = AngDiff(lon1, lon2, &lon12s); - /* Make longitude difference positive. */ - lonsign = lon12 >= 0 ? 1 : -1; - /* If very close to being on the same half-meridian, then make it so. */ - lon12 = lonsign * AngRound(lon12); - lon12s = AngRound((180 - lon12) - lonsign * lon12s); - lam12 = lon12 * degree; - if (lon12 > 90) { - sincosdx(lon12s, &slam12, &clam12); - clam12 = -clam12; - } else - sincosdx(lon12, &slam12, &clam12); - - /* If really close to the equator, treat as on equator. */ - lat1 = AngRound(LatFix(lat1)); - lat2 = AngRound(LatFix(lat2)); - /* Swap points so that point with higher (abs) latitude is point 1 - * If one latitude is a nan, then it becomes lat1. */ - swapp = fabs(lat1) < fabs(lat2) ? -1 : 1; - if (swapp < 0) { - lonsign *= -1; - swapx(&lat1, &lat2); - } - /* Make lat1 <= 0 */ - latsign = lat1 < 0 ? 1 : -1; - lat1 *= latsign; - lat2 *= latsign; - /* Now we have - * - * 0 <= lon12 <= 180 - * -90 <= lat1 <= 0 - * lat1 <= lat2 <= -lat1 - * - * longsign, swapp, latsign register the transformation to bring the - * coordinates to this canonical form. In all cases, 1 means no change was - * made. We make these transformations so that there are few cases to - * check, e.g., on verifying quadrants in atan2. In addition, this - * enforces some symmetries in the results returned. */ - - sincosdx(lat1, &sbet1, &cbet1); sbet1 *= g->f1; - /* Ensure cbet1 = +epsilon at poles */ - norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1); - - sincosdx(lat2, &sbet2, &cbet2); sbet2 *= g->f1; - /* Ensure cbet2 = +epsilon at poles */ - norm2(&sbet2, &cbet2); cbet2 = maxx(tiny, cbet2); - - /* If cbet1 < -sbet1, then cbet2 - cbet1 is a sensitive measure of the - * |bet1| - |bet2|. Alternatively (cbet1 >= -sbet1), abs(sbet2) + sbet1 is - * a better measure. This logic is used in assigning calp2 in Lambda12. - * Sometimes these quantities vanish and in that case we force bet2 = +/- - * bet1 exactly. An example where is is necessary is the inverse problem - * 48.522876735459 0 -48.52287673545898293 179.599720456223079643 - * which failed with Visual Studio 10 (Release and Debug) */ - - if (cbet1 < -sbet1) { - if (cbet2 == cbet1) - sbet2 = sbet2 < 0 ? sbet1 : -sbet1; - } else { - if (fabs(sbet2) == -sbet1) - cbet2 = cbet1; - } - - dn1 = sqrt(1 + g->ep2 * sq(sbet1)); - dn2 = sqrt(1 + g->ep2 * sq(sbet2)); - - meridian = lat1 == -90 || slam12 == 0; - - if (meridian) { - - /* Endpoints are on a single full meridian, so the geodesic might lie on - * a meridian. */ - - real ssig1, csig1, ssig2, csig2; - calp1 = clam12; salp1 = slam12; /* Head to the target longitude */ - calp2 = 1; salp2 = 0; /* At the target we're heading north */ - - /* tan(bet) = tan(sig) * cos(alp) */ - ssig1 = sbet1; csig1 = calp1 * cbet1; - ssig2 = sbet2; csig2 = calp2 * cbet2; - - /* sig12 = sig2 - sig1 */ - sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2), - csig1 * csig2 + ssig1 * ssig2); - Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, - cbet1, cbet2, &s12x, &m12x, 0, - (outmask & GEOD_GEODESICSCALE) ? &M12 : 0, - (outmask & GEOD_GEODESICSCALE) ? &M21 : 0, - Ca); - /* Add the check for sig12 since zero length geodesics might yield m12 < - * 0. Test case was - * - * echo 20.001 0 20.001 0 | GeodSolve -i - * - * In fact, we will have sig12 > pi/2 for meridional geodesic which is - * not a shortest path. */ - if (sig12 < 1 || m12x >= 0) { - /* Need at least 2, to handle 90 0 90 180 */ - if (sig12 < 3 * tiny) - sig12 = m12x = s12x = 0; - m12x *= g->b; - s12x *= g->b; - a12 = sig12 / degree; - } else - /* m12 < 0, i.e., prolate and too close to anti-podal */ - meridian = FALSE; - } - - if (!meridian && - sbet1 == 0 && /* and sbet2 == 0 */ - /* Mimic the way Lambda12 works with calp1 = 0 */ - (g->f <= 0 || lon12s >= g->f * 180)) { - - /* Geodesic runs along equator */ - calp1 = calp2 = 0; salp1 = salp2 = 1; - s12x = g->a * lam12; - sig12 = omg12 = lam12 / g->f1; - m12x = g->b * sin(sig12); - if (outmask & GEOD_GEODESICSCALE) - M12 = M21 = cos(sig12); - a12 = lon12 / g->f1; - - } else if (!meridian) { - - /* Now point1 and point2 belong within a hemisphere bounded by a - * meridian and geodesic is neither meridional or equatorial. */ - - /* Figure a starting point for Newton's method */ - real dnm = 0; - sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, - lam12, slam12, clam12, - &salp1, &calp1, &salp2, &calp2, &dnm, - Ca); - - if (sig12 >= 0) { - /* Short lines (InverseStart sets salp2, calp2, dnm) */ - s12x = sig12 * g->b * dnm; - m12x = sq(dnm) * g->b * sin(sig12 / dnm); - if (outmask & GEOD_GEODESICSCALE) - M12 = M21 = cos(sig12 / dnm); - a12 = sig12 / degree; - omg12 = lam12 / (g->f1 * dnm); - } else { - - /* Newton's method. This is a straightforward solution of f(alp1) = - * lambda12(alp1) - lam12 = 0 with one wrinkle. f(alp) has exactly one - * root in the interval (0, pi) and its derivative is positive at the - * root. Thus f(alp) is positive for alp > alp1 and negative for alp < - * alp1. During the course of the iteration, a range (alp1a, alp1b) is - * maintained which brackets the root and with each evaluation of - * f(alp) the range is shrunk, if possible. Newton's method is - * restarted whenever the derivative of f is negative (because the new - * value of alp1 is then further from the solution) or if the new - * estimate of alp1 lies outside (0,pi); in this case, the new starting - * guess is taken to be (alp1a + alp1b) / 2. */ - real ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, domg12 = 0; - unsigned numit = 0; - /* Bracketing range */ - real salp1a = tiny, calp1a = 1, salp1b = tiny, calp1b = -1; - boolx tripn = FALSE; - boolx tripb = FALSE; - for (; numit < maxit2; ++numit) { - /* the WGS84 test set: mean = 1.47, sd = 1.25, max = 16 - * WGS84 and random input: mean = 2.85, sd = 0.60 */ - real dv = 0, - v = Lambda12(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1, - slam12, clam12, - &salp2, &calp2, &sig12, &ssig1, &csig1, &ssig2, &csig2, - &eps, &domg12, numit < maxit1, &dv, Ca); - /* 2 * tol0 is approximately 1 ulp for a number in [0, pi]. */ - /* Reversed test to allow escape with NaNs */ - if (tripb || !(fabs(v) >= (tripn ? 8 : 1) * tol0)) break; - /* Update bracketing values */ - if (v > 0 && (numit > maxit1 || calp1/salp1 > calp1b/salp1b)) - { salp1b = salp1; calp1b = calp1; } - else if (v < 0 && (numit > maxit1 || calp1/salp1 < calp1a/salp1a)) - { salp1a = salp1; calp1a = calp1; } - if (numit < maxit1 && dv > 0) { - real - dalp1 = -v/dv; - real - sdalp1 = sin(dalp1), cdalp1 = cos(dalp1), - nsalp1 = salp1 * cdalp1 + calp1 * sdalp1; - if (nsalp1 > 0 && fabs(dalp1) < pi) { - calp1 = calp1 * cdalp1 - salp1 * sdalp1; - salp1 = nsalp1; - norm2(&salp1, &calp1); - /* In some regimes we don't get quadratic convergence because - * slope -> 0. So use convergence conditions based on epsilon - * instead of sqrt(epsilon). */ - tripn = fabs(v) <= 16 * tol0; - continue; - } - } - /* Either dv was not positive or updated value was outside legal - * range. Use the midpoint of the bracket as the next estimate. - * This mechanism is not needed for the WGS84 ellipsoid, but it does - * catch problems with more eccentric ellipsoids. Its efficacy is - * such for the WGS84 test set with the starting guess set to alp1 = - * 90deg: - * the WGS84 test set: mean = 5.21, sd = 3.93, max = 24 - * WGS84 and random input: mean = 4.74, sd = 0.99 */ - salp1 = (salp1a + salp1b)/2; - calp1 = (calp1a + calp1b)/2; - norm2(&salp1, &calp1); - tripn = FALSE; - tripb = (fabs(salp1a - salp1) + (calp1a - calp1) < tolb || - fabs(salp1 - salp1b) + (calp1 - calp1b) < tolb); - } - Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, - cbet1, cbet2, &s12x, &m12x, 0, - (outmask & GEOD_GEODESICSCALE) ? &M12 : 0, - (outmask & GEOD_GEODESICSCALE) ? &M21 : 0, Ca); - m12x *= g->b; - s12x *= g->b; - a12 = sig12 / degree; - if (outmask & GEOD_AREA) { - /* omg12 = lam12 - domg12 */ - real sdomg12 = sin(domg12), cdomg12 = cos(domg12); - somg12 = slam12 * cdomg12 - clam12 * sdomg12; - comg12 = clam12 * cdomg12 + slam12 * sdomg12; - } - } - } - - if (outmask & GEOD_DISTANCE) - s12 = 0 + s12x; /* Convert -0 to 0 */ - - if (outmask & GEOD_REDUCEDLENGTH) - m12 = 0 + m12x; /* Convert -0 to 0 */ - - if (outmask & GEOD_AREA) { - real - /* From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) */ - salp0 = salp1 * cbet1, - calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */ - real alp12; - if (calp0 != 0 && salp0 != 0) { - real - /* From Lambda12: tan(bet) = tan(sig) * cos(alp) */ - ssig1 = sbet1, csig1 = calp1 * cbet1, - ssig2 = sbet2, csig2 = calp2 * cbet2, - k2 = sq(calp0) * g->ep2, - eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2), - /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). */ - A4 = sq(g->a) * calp0 * salp0 * g->e2; - real B41, B42; - norm2(&ssig1, &csig1); - norm2(&ssig2, &csig2); - C4f(g, eps, Ca); - B41 = SinCosSeries(FALSE, ssig1, csig1, Ca, nC4); - B42 = SinCosSeries(FALSE, ssig2, csig2, Ca, nC4); - S12 = A4 * (B42 - B41); - } else - /* Avoid problems with indeterminate sig1, sig2 on equator */ - S12 = 0; - - if (!meridian && somg12 > 1) { - somg12 = sin(omg12); comg12 = cos(omg12); - } - - if (!meridian && - /* omg12 < 3/4 * pi */ - comg12 > -(real)(0.7071) && /* Long difference not too big */ - sbet2 - sbet1 < (real)(1.75)) { /* Lat difference not too big */ - /* Use tan(Gamma/2) = tan(omg12/2) - * * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2)) - * with tan(x/2) = sin(x)/(1+cos(x)) */ - real - domg12 = 1 + comg12, dbet1 = 1 + cbet1, dbet2 = 1 + cbet2; - alp12 = 2 * atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ), - domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) ); - } else { - /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ - real - salp12 = salp2 * calp1 - calp2 * salp1, - calp12 = calp2 * calp1 + salp2 * salp1; - /* The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz - * salp12 = -0 and alp12 = -180. However this depends on the sign - * being attached to 0 correctly. The following ensures the correct - * behavior. */ - if (salp12 == 0 && calp12 < 0) { - salp12 = tiny * calp1; - calp12 = -1; - } - alp12 = atan2(salp12, calp12); - } - S12 += g->c2 * alp12; - S12 *= swapp * lonsign * latsign; - /* Convert -0 to 0 */ - S12 += 0; - } - - /* Convert calp, salp to azimuth accounting for lonsign, swapp, latsign. */ - if (swapp < 0) { - swapx(&salp1, &salp2); - swapx(&calp1, &calp2); - if (outmask & GEOD_GEODESICSCALE) - swapx(&M12, &M21); - } - - salp1 *= swapp * lonsign; calp1 *= swapp * latsign; - salp2 *= swapp * lonsign; calp2 *= swapp * latsign; - - if (psalp1) *psalp1 = salp1; - if (pcalp1) *pcalp1 = calp1; - if (psalp2) *psalp2 = salp2; - if (pcalp2) *pcalp2 = calp2; - - if (outmask & GEOD_DISTANCE) - *ps12 = s12; - if (outmask & GEOD_REDUCEDLENGTH) - *pm12 = m12; - if (outmask & GEOD_GEODESICSCALE) { - if (pM12) *pM12 = M12; - if (pM21) *pM21 = M21; - } - if (outmask & GEOD_AREA) - *pS12 = S12; - - /* Returned value in [0, 180] */ - return a12; -} - -real geod_geninverse(const struct geod_geodesic* g, - real lat1, real lon1, real lat2, real lon2, - real* ps12, real* pazi1, real* pazi2, - real* pm12, real* pM12, real* pM21, real* pS12) { - real salp1, calp1, salp2, calp2, - a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, ps12, - &salp1, &calp1, &salp2, &calp2, - pm12, pM12, pM21, pS12); - if (pazi1) *pazi1 = atan2dx(salp1, calp1); - if (pazi2) *pazi2 = atan2dx(salp2, calp2); - return a12; -} - -void geod_inverseline(struct geod_geodesicline* l, - const struct geod_geodesic* g, - real lat1, real lon1, real lat2, real lon2, - unsigned caps) { - real salp1, calp1, - a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, 0, - &salp1, &calp1, 0, 0, - 0, 0, 0, 0), - azi1 = atan2dx(salp1, calp1); - caps = caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE; - /* Ensure that a12 can be converted to a distance */ - if (caps & (OUT_ALL & GEOD_DISTANCE_IN)) caps |= GEOD_DISTANCE; - geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); - geod_setarc(l, a12); -} - -void geod_inverse(const struct geod_geodesic* g, - real lat1, real lon1, real lat2, real lon2, - real* ps12, real* pazi1, real* pazi2) { - geod_geninverse(g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, 0, 0, 0, 0); -} - -real SinCosSeries(boolx sinp, real sinx, real cosx, const real c[], int n) { - /* Evaluate - * y = sinp ? sum(c[i] * sin( 2*i * x), i, 1, n) : - * sum(c[i] * cos((2*i+1) * x), i, 0, n-1) - * using Clenshaw summation. N.B. c[0] is unused for sin series - * Approx operation count = (n + 5) mult and (2 * n + 2) add */ - real ar, y0, y1; - c += (n + sinp); /* Point to one beyond last element */ - ar = 2 * (cosx - sinx) * (cosx + sinx); /* 2 * cos(2 * x) */ - y0 = (n & 1) ? *--c : 0; y1 = 0; /* accumulators for sum */ - /* Now n is even */ - n /= 2; - while (n--) { - /* Unroll loop x 2, so accumulators return to their original role */ - y1 = ar * y0 - y1 + *--c; - y0 = ar * y1 - y0 + *--c; - } - return sinp - ? 2 * sinx * cosx * y0 /* sin(2 * x) * y0 */ - : cosx * (y0 - y1); /* cos(x) * (y0 - y1) */ -} - -void Lengths(const struct geod_geodesic* g, - real eps, real sig12, - real ssig1, real csig1, real dn1, - real ssig2, real csig2, real dn2, - real cbet1, real cbet2, - real* ps12b, real* pm12b, real* pm0, - real* pM12, real* pM21, - /* Scratch area of the right size */ - real Ca[]) { - real m0 = 0, J12 = 0, A1 = 0, A2 = 0; - real Cb[nC]; - - /* Return m12b = (reduced length)/b; also calculate s12b = distance/b, - * and m0 = coefficient of secular term in expression for reduced length. */ - boolx redlp = pm12b || pm0 || pM12 || pM21; - if (ps12b || redlp) { - A1 = A1m1f(eps); - C1f(eps, Ca); - if (redlp) { - A2 = A2m1f(eps); - C2f(eps, Cb); - m0 = A1 - A2; - A2 = 1 + A2; - } - A1 = 1 + A1; - } - if (ps12b) { - real B1 = SinCosSeries(TRUE, ssig2, csig2, Ca, nC1) - - SinCosSeries(TRUE, ssig1, csig1, Ca, nC1); - /* Missing a factor of b */ - *ps12b = A1 * (sig12 + B1); - if (redlp) { - real B2 = SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - - SinCosSeries(TRUE, ssig1, csig1, Cb, nC2); - J12 = m0 * sig12 + (A1 * B1 - A2 * B2); - } - } else if (redlp) { - /* Assume here that nC1 >= nC2 */ - int l; - for (l = 1; l <= nC2; ++l) - Cb[l] = A1 * Ca[l] - A2 * Cb[l]; - J12 = m0 * sig12 + (SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - - SinCosSeries(TRUE, ssig1, csig1, Cb, nC2)); - } - if (pm0) *pm0 = m0; - if (pm12b) - /* Missing a factor of b. - * Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure - * accurate cancellation in the case of coincident points. */ - *pm12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - - csig1 * csig2 * J12; - if (pM12 || pM21) { - real csig12 = csig1 * csig2 + ssig1 * ssig2; - real t = g->ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2); - if (pM12) - *pM12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1; - if (pM21) - *pM21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2; - } -} - -real Astroid(real x, real y) { - /* Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive root k. - * This solution is adapted from Geocentric::Reverse. */ - real k; - real - p = sq(x), - q = sq(y), - r = (p + q - 1) / 6; - if ( !(q == 0 && r <= 0) ) { - real - /* Avoid possible division by zero when r = 0 by multiplying equations - * for s and t by r^3 and r, resp. */ - S = p * q / 4, /* S = r^3 * s */ - r2 = sq(r), - r3 = r * r2, - /* The discriminant of the quadratic equation for T3. This is zero on - * the evolute curve p^(1/3)+q^(1/3) = 1 */ - disc = S * (S + 2 * r3); - real u = r; - real v, uv, w; - if (disc >= 0) { - real T3 = S + r3, T; - /* Pick the sign on the sqrt to maximize abs(T3). This minimizes loss - * of precision due to cancellation. The result is unchanged because - * of the way the T is used in definition of u. */ - T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); /* T3 = (r * t)^3 */ - /* N.B. cbrtx always returns the real root. cbrtx(-8) = -2. */ - T = cbrtx(T3); /* T = r * t */ - /* T can be zero; but then r2 / T -> 0. */ - u += T + (T != 0 ? r2 / T : 0); - } else { - /* T is complex, but the way u is defined the result is real. */ - real ang = atan2(sqrt(-disc), -(S + r3)); - /* There are three possible cube roots. We choose the root which - * avoids cancellation. Note that disc < 0 implies that r < 0. */ - u += 2 * r * cos(ang / 3); - } - v = sqrt(sq(u) + q); /* guaranteed positive */ - /* Avoid loss of accuracy when u < 0. */ - uv = u < 0 ? q / (v - u) : u + v; /* u+v, guaranteed positive */ - w = (uv - q) / (2 * v); /* positive? */ - /* Rearrange expression for k to avoid loss of accuracy due to - * subtraction. Division by 0 not possible because uv > 0, w >= 0. */ - k = uv / (sqrt(uv + sq(w)) + w); /* guaranteed positive */ - } else { /* q == 0 && r <= 0 */ - /* y = 0 with |x| <= 1. Handle this case directly. - * for y small, positive root is k = abs(y)/sqrt(1-x^2) */ - k = 0; - } - return k; -} - -real InverseStart(const struct geod_geodesic* g, - real sbet1, real cbet1, real dn1, - real sbet2, real cbet2, real dn2, - real lam12, real slam12, real clam12, - real* psalp1, real* pcalp1, - /* Only updated if return val >= 0 */ - real* psalp2, real* pcalp2, - /* Only updated for short lines */ - real* pdnm, - /* Scratch area of the right size */ - real Ca[]) { - real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0, dnm = 0; - - /* Return a starting point for Newton's method in salp1 and calp1 (function - * value is -1). If Newton's method doesn't need to be used, return also - * salp2 and calp2 and function value is sig12. */ - real - sig12 = -1, /* Return value */ - /* bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0] */ - sbet12 = sbet2 * cbet1 - cbet2 * sbet1, - cbet12 = cbet2 * cbet1 + sbet2 * sbet1; - real sbet12a; - boolx shortline = cbet12 >= 0 && sbet12 < (real)(0.5) && - cbet2 * lam12 < (real)(0.5); - real somg12, comg12, ssig12, csig12; -#if defined(__GNUC__) && __GNUC__ == 4 && \ - (__GNUC_MINOR__ < 6 || defined(__MINGW32__)) - /* Volatile declaration needed to fix inverse cases - * 88.202499451857 0 -88.202499451857 179.981022032992859592 - * 89.262080389218 0 -89.262080389218 179.992207982775375662 - * 89.333123580033 0 -89.333123580032997687 179.99295812360148422 - * which otherwise fail with g++ 4.4.4 x86 -O3 (Linux) - * and g++ 4.4.0 (mingw) and g++ 4.6.1 (tdm mingw). */ - { - volatile real xx1 = sbet2 * cbet1; - volatile real xx2 = cbet2 * sbet1; - sbet12a = xx1 + xx2; - } -#else - sbet12a = sbet2 * cbet1 + cbet2 * sbet1; -#endif - if (shortline) { - real sbetm2 = sq(sbet1 + sbet2), omg12; - /* sin((bet1+bet2)/2)^2 - * = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) */ - sbetm2 /= sbetm2 + sq(cbet1 + cbet2); - dnm = sqrt(1 + g->ep2 * sbetm2); - omg12 = lam12 / (g->f1 * dnm); - somg12 = sin(omg12); comg12 = cos(omg12); - } else { - somg12 = slam12; comg12 = clam12; - } - - salp1 = cbet2 * somg12; - calp1 = comg12 >= 0 ? - sbet12 + cbet2 * sbet1 * sq(somg12) / (1 + comg12) : - sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); - - ssig12 = hypotx(salp1, calp1); - csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; - - if (shortline && ssig12 < g->etol2) { - /* really short lines */ - salp2 = cbet1 * somg12; - calp2 = sbet12 - cbet1 * sbet2 * - (comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12); - norm2(&salp2, &calp2); - /* Set return value */ - sig12 = atan2(ssig12, csig12); - } else if (fabs(g->n) > (real)(0.1) || /* No astroid calc if too eccentric */ - csig12 >= 0 || - ssig12 >= 6 * fabs(g->n) * pi * sq(cbet1)) { - /* Nothing to do, zeroth order spherical approximation is OK */ - } else { - /* Scale lam12 and bet2 to x, y coordinate system where antipodal point - * is at origin and singular point is at y = 0, x = -1. */ - real y, lamscale, betscale; - /* Volatile declaration needed to fix inverse case - * 56.320923501171 0 -56.320923501171 179.664747671772880215 - * which otherwise fails with g++ 4.4.4 x86 -O3 */ - volatile real x; - real lam12x = atan2(-slam12, -clam12); /* lam12 - pi */ - if (g->f >= 0) { /* In fact f == 0 does not get here */ - /* x = dlong, y = dlat */ - { - real - k2 = sq(sbet1) * g->ep2, - eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); - lamscale = g->f * cbet1 * A3f(g, eps) * pi; - } - betscale = lamscale * cbet1; - - x = lam12x / lamscale; - y = sbet12a / betscale; - } else { /* f < 0 */ - /* x = dlat, y = dlong */ - real - cbet12a = cbet2 * cbet1 - sbet2 * sbet1, - bet12a = atan2(sbet12a, cbet12a); - real m12b, m0; - /* In the case of lon12 = 180, this repeats a calculation made in - * Inverse. */ - Lengths(g, g->n, pi + bet12a, - sbet1, -cbet1, dn1, sbet2, cbet2, dn2, - cbet1, cbet2, 0, &m12b, &m0, 0, 0, Ca); - x = -1 + m12b / (cbet1 * cbet2 * m0 * pi); - betscale = x < -(real)(0.01) ? sbet12a / x : - -g->f * sq(cbet1) * pi; - lamscale = betscale / cbet1; - y = lam12x / lamscale; - } - - if (y > -tol1 && x > -1 - xthresh) { - /* strip near cut */ - if (g->f >= 0) { - salp1 = minx((real)(1), -(real)(x)); calp1 = - sqrt(1 - sq(salp1)); - } else { - calp1 = maxx((real)(x > -tol1 ? 0 : -1), (real)(x)); - salp1 = sqrt(1 - sq(calp1)); - } - } else { - /* Estimate alp1, by solving the astroid problem. - * - * Could estimate alpha1 = theta + pi/2, directly, i.e., - * calp1 = y/k; salp1 = -x/(1+k); for f >= 0 - * calp1 = x/(1+k); salp1 = -y/k; for f < 0 (need to check) - * - * However, it's better to estimate omg12 from astroid and use - * spherical formula to compute alp1. This reduces the mean number of - * Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12 - * (min 0 max 5). The changes in the number of iterations are as - * follows: - * - * change percent - * 1 5 - * 0 78 - * -1 16 - * -2 0.6 - * -3 0.04 - * -4 0.002 - * - * The histogram of iterations is (m = number of iterations estimating - * alp1 directly, n = number of iterations estimating via omg12, total - * number of trials = 148605): - * - * iter m n - * 0 148 186 - * 1 13046 13845 - * 2 93315 102225 - * 3 36189 32341 - * 4 5396 7 - * 5 455 1 - * 6 56 0 - * - * Because omg12 is near pi, estimate work with omg12a = pi - omg12 */ - real k = Astroid(x, y); - real - omg12a = lamscale * ( g->f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k ); - somg12 = sin(omg12a); comg12 = -cos(omg12a); - /* Update spherical estimate of alp1 using omg12 instead of lam12 */ - salp1 = cbet2 * somg12; - calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); - } - } - /* Sanity check on starting guess. Backwards check allows NaN through. */ - if (!(salp1 <= 0)) - norm2(&salp1, &calp1); - else { - salp1 = 1; calp1 = 0; - } - - *psalp1 = salp1; - *pcalp1 = calp1; - if (shortline) - *pdnm = dnm; - if (sig12 >= 0) { - *psalp2 = salp2; - *pcalp2 = calp2; - } - return sig12; -} - -real Lambda12(const struct geod_geodesic* g, - real sbet1, real cbet1, real dn1, - real sbet2, real cbet2, real dn2, - real salp1, real calp1, - real slam120, real clam120, - real* psalp2, real* pcalp2, - real* psig12, - real* pssig1, real* pcsig1, - real* pssig2, real* pcsig2, - real* peps, - real* pdomg12, - boolx diffp, real* pdlam12, - /* Scratch area of the right size */ - real Ca[]) { - real salp2 = 0, calp2 = 0, sig12 = 0, - ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, - domg12 = 0, dlam12 = 0; - real salp0, calp0; - real somg1, comg1, somg2, comg2, somg12, comg12, lam12; - real B312, eta, k2; - - if (sbet1 == 0 && calp1 == 0) - /* Break degeneracy of equatorial line. This case has already been - * handled. */ - calp1 = -tiny; - - /* sin(alp1) * cos(bet1) = sin(alp0) */ - salp0 = salp1 * cbet1; - calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */ - - /* tan(bet1) = tan(sig1) * cos(alp1) - * tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1) */ - ssig1 = sbet1; somg1 = salp0 * sbet1; - csig1 = comg1 = calp1 * cbet1; - norm2(&ssig1, &csig1); - /* norm2(&somg1, &comg1); -- don't need to normalize! */ - - /* Enforce symmetries in the case abs(bet2) = -bet1. Need to be careful - * about this case, since this can yield singularities in the Newton - * iteration. - * sin(alp2) * cos(bet2) = sin(alp0) */ - salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1; - /* calp2 = sqrt(1 - sq(salp2)) - * = sqrt(sq(calp0) - sq(sbet2)) / cbet2 - * and subst for calp0 and rearrange to give (choose positive sqrt - * to give alp2 in [0, pi/2]). */ - calp2 = cbet2 != cbet1 || fabs(sbet2) != -sbet1 ? - sqrt(sq(calp1 * cbet1) + - (cbet1 < -sbet1 ? - (cbet2 - cbet1) * (cbet1 + cbet2) : - (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 : - fabs(calp1); - /* tan(bet2) = tan(sig2) * cos(alp2) - * tan(omg2) = sin(alp0) * tan(sig2). */ - ssig2 = sbet2; somg2 = salp0 * sbet2; - csig2 = comg2 = calp2 * cbet2; - norm2(&ssig2, &csig2); - /* norm2(&somg2, &comg2); -- don't need to normalize! */ - - /* sig12 = sig2 - sig1, limit to [0, pi] */ - sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2), - csig1 * csig2 + ssig1 * ssig2); - - /* omg12 = omg2 - omg1, limit to [0, pi] */ - somg12 = maxx((real)(0), comg1 * somg2 - somg1 * comg2); - comg12 = comg1 * comg2 + somg1 * somg2; - /* eta = omg12 - lam120 */ - eta = atan2(somg12 * clam120 - comg12 * slam120, - comg12 * clam120 + somg12 * slam120); - k2 = sq(calp0) * g->ep2; - eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); - C3f(g, eps, Ca); - B312 = (SinCosSeries(TRUE, ssig2, csig2, Ca, nC3-1) - - SinCosSeries(TRUE, ssig1, csig1, Ca, nC3-1)); - domg12 = -g->f * A3f(g, eps) * salp0 * (sig12 + B312); - lam12 = eta + domg12; - - if (diffp) { - if (calp2 == 0) - dlam12 = - 2 * g->f1 * dn1 / sbet1; - else { - Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, - cbet1, cbet2, 0, &dlam12, 0, 0, 0, Ca); - dlam12 *= g->f1 / (calp2 * cbet2); - } - } - - *psalp2 = salp2; - *pcalp2 = calp2; - *psig12 = sig12; - *pssig1 = ssig1; - *pcsig1 = csig1; - *pssig2 = ssig2; - *pcsig2 = csig2; - *peps = eps; - *pdomg12 = domg12; - if (diffp) - *pdlam12 = dlam12; - - return lam12; -} - -real A3f(const struct geod_geodesic* g, real eps) { - /* Evaluate A3 */ - return polyval(nA3 - 1, g->A3x, eps); -} - -void C3f(const struct geod_geodesic* g, real eps, real c[]) { - /* Evaluate C3 coeffs - * Elements c[1] through c[nC3 - 1] are set */ - real mult = 1; - int o = 0, l; - for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ - int m = nC3 - l - 1; /* order of polynomial in eps */ - mult *= eps; - c[l] = mult * polyval(m, g->C3x + o, eps); - o += m + 1; - } -} - -void C4f(const struct geod_geodesic* g, real eps, real c[]) { - /* Evaluate C4 coeffs - * Elements c[0] through c[nC4 - 1] are set */ - real mult = 1; - int o = 0, l; - for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ - int m = nC4 - l - 1; /* order of polynomial in eps */ - c[l] = mult * polyval(m, g->C4x + o, eps); - o += m + 1; - mult *= eps; - } -} - -/* The scale factor A1-1 = mean value of (d/dsigma)I1 - 1 */ -real A1m1f(real eps) { - static const real coeff[] = { - /* (1-eps)*A1-1, polynomial in eps2 of order 3 */ - 1, 4, 64, 0, 256, - }; - int m = nA1/2; - real t = polyval(m, coeff, sq(eps)) / coeff[m + 1]; - return (t + eps) / (1 - eps); -} - -/* The coefficients C1[l] in the Fourier expansion of B1 */ -void C1f(real eps, real c[]) { - static const real coeff[] = { - /* C1[1]/eps^1, polynomial in eps2 of order 2 */ - -1, 6, -16, 32, - /* C1[2]/eps^2, polynomial in eps2 of order 2 */ - -9, 64, -128, 2048, - /* C1[3]/eps^3, polynomial in eps2 of order 1 */ - 9, -16, 768, - /* C1[4]/eps^4, polynomial in eps2 of order 1 */ - 3, -5, 512, - /* C1[5]/eps^5, polynomial in eps2 of order 0 */ - -7, 1280, - /* C1[6]/eps^6, polynomial in eps2 of order 0 */ - -7, 2048, - }; - real - eps2 = sq(eps), - d = eps; - int o = 0, l; - for (l = 1; l <= nC1; ++l) { /* l is index of C1p[l] */ - int m = (nC1 - l) / 2; /* order of polynomial in eps^2 */ - c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; - o += m + 2; - d *= eps; - } -} - -/* The coefficients C1p[l] in the Fourier expansion of B1p */ -void C1pf(real eps, real c[]) { - static const real coeff[] = { - /* C1p[1]/eps^1, polynomial in eps2 of order 2 */ - 205, -432, 768, 1536, - /* C1p[2]/eps^2, polynomial in eps2 of order 2 */ - 4005, -4736, 3840, 12288, - /* C1p[3]/eps^3, polynomial in eps2 of order 1 */ - -225, 116, 384, - /* C1p[4]/eps^4, polynomial in eps2 of order 1 */ - -7173, 2695, 7680, - /* C1p[5]/eps^5, polynomial in eps2 of order 0 */ - 3467, 7680, - /* C1p[6]/eps^6, polynomial in eps2 of order 0 */ - 38081, 61440, - }; - real - eps2 = sq(eps), - d = eps; - int o = 0, l; - for (l = 1; l <= nC1p; ++l) { /* l is index of C1p[l] */ - int m = (nC1p - l) / 2; /* order of polynomial in eps^2 */ - c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; - o += m + 2; - d *= eps; - } -} - -/* The scale factor A2-1 = mean value of (d/dsigma)I2 - 1 */ -real A2m1f(real eps) { - static const real coeff[] = { - /* (eps+1)*A2-1, polynomial in eps2 of order 3 */ - -11, -28, -192, 0, 256, - }; - int m = nA2/2; - real t = polyval(m, coeff, sq(eps)) / coeff[m + 1]; - return (t - eps) / (1 + eps); -} - -/* The coefficients C2[l] in the Fourier expansion of B2 */ -void C2f(real eps, real c[]) { - static const real coeff[] = { - /* C2[1]/eps^1, polynomial in eps2 of order 2 */ - 1, 2, 16, 32, - /* C2[2]/eps^2, polynomial in eps2 of order 2 */ - 35, 64, 384, 2048, - /* C2[3]/eps^3, polynomial in eps2 of order 1 */ - 15, 80, 768, - /* C2[4]/eps^4, polynomial in eps2 of order 1 */ - 7, 35, 512, - /* C2[5]/eps^5, polynomial in eps2 of order 0 */ - 63, 1280, - /* C2[6]/eps^6, polynomial in eps2 of order 0 */ - 77, 2048, - }; - real - eps2 = sq(eps), - d = eps; - int o = 0, l; - for (l = 1; l <= nC2; ++l) { /* l is index of C2[l] */ - int m = (nC2 - l) / 2; /* order of polynomial in eps^2 */ - c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; - o += m + 2; - d *= eps; - } -} - -/* The scale factor A3 = mean value of (d/dsigma)I3 */ -void A3coeff(struct geod_geodesic* g) { - static const real coeff[] = { - /* A3, coeff of eps^5, polynomial in n of order 0 */ - -3, 128, - /* A3, coeff of eps^4, polynomial in n of order 1 */ - -2, -3, 64, - /* A3, coeff of eps^3, polynomial in n of order 2 */ - -1, -3, -1, 16, - /* A3, coeff of eps^2, polynomial in n of order 2 */ - 3, -1, -2, 8, - /* A3, coeff of eps^1, polynomial in n of order 1 */ - 1, -1, 2, - /* A3, coeff of eps^0, polynomial in n of order 0 */ - 1, 1, - }; - int o = 0, k = 0, j; - for (j = nA3 - 1; j >= 0; --j) { /* coeff of eps^j */ - int m = nA3 - j - 1 < j ? nA3 - j - 1 : j; /* order of polynomial in n */ - g->A3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; - o += m + 2; - } -} - -/* The coefficients C3[l] in the Fourier expansion of B3 */ -void C3coeff(struct geod_geodesic* g) { - static const real coeff[] = { - /* C3[1], coeff of eps^5, polynomial in n of order 0 */ - 3, 128, - /* C3[1], coeff of eps^4, polynomial in n of order 1 */ - 2, 5, 128, - /* C3[1], coeff of eps^3, polynomial in n of order 2 */ - -1, 3, 3, 64, - /* C3[1], coeff of eps^2, polynomial in n of order 2 */ - -1, 0, 1, 8, - /* C3[1], coeff of eps^1, polynomial in n of order 1 */ - -1, 1, 4, - /* C3[2], coeff of eps^5, polynomial in n of order 0 */ - 5, 256, - /* C3[2], coeff of eps^4, polynomial in n of order 1 */ - 1, 3, 128, - /* C3[2], coeff of eps^3, polynomial in n of order 2 */ - -3, -2, 3, 64, - /* C3[2], coeff of eps^2, polynomial in n of order 2 */ - 1, -3, 2, 32, - /* C3[3], coeff of eps^5, polynomial in n of order 0 */ - 7, 512, - /* C3[3], coeff of eps^4, polynomial in n of order 1 */ - -10, 9, 384, - /* C3[3], coeff of eps^3, polynomial in n of order 2 */ - 5, -9, 5, 192, - /* C3[4], coeff of eps^5, polynomial in n of order 0 */ - 7, 512, - /* C3[4], coeff of eps^4, polynomial in n of order 1 */ - -14, 7, 512, - /* C3[5], coeff of eps^5, polynomial in n of order 0 */ - 21, 2560, - }; - int o = 0, k = 0, l, j; - for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ - for (j = nC3 - 1; j >= l; --j) { /* coeff of eps^j */ - int m = nC3 - j - 1 < j ? nC3 - j - 1 : j; /* order of polynomial in n */ - g->C3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; - o += m + 2; - } - } -} - -/* The coefficients C4[l] in the Fourier expansion of I4 */ -void C4coeff(struct geod_geodesic* g) { - static const real coeff[] = { - /* C4[0], coeff of eps^5, polynomial in n of order 0 */ - 97, 15015, - /* C4[0], coeff of eps^4, polynomial in n of order 1 */ - 1088, 156, 45045, - /* C4[0], coeff of eps^3, polynomial in n of order 2 */ - -224, -4784, 1573, 45045, - /* C4[0], coeff of eps^2, polynomial in n of order 3 */ - -10656, 14144, -4576, -858, 45045, - /* C4[0], coeff of eps^1, polynomial in n of order 4 */ - 64, 624, -4576, 6864, -3003, 15015, - /* C4[0], coeff of eps^0, polynomial in n of order 5 */ - 100, 208, 572, 3432, -12012, 30030, 45045, - /* C4[1], coeff of eps^5, polynomial in n of order 0 */ - 1, 9009, - /* C4[1], coeff of eps^4, polynomial in n of order 1 */ - -2944, 468, 135135, - /* C4[1], coeff of eps^3, polynomial in n of order 2 */ - 5792, 1040, -1287, 135135, - /* C4[1], coeff of eps^2, polynomial in n of order 3 */ - 5952, -11648, 9152, -2574, 135135, - /* C4[1], coeff of eps^1, polynomial in n of order 4 */ - -64, -624, 4576, -6864, 3003, 135135, - /* C4[2], coeff of eps^5, polynomial in n of order 0 */ - 8, 10725, - /* C4[2], coeff of eps^4, polynomial in n of order 1 */ - 1856, -936, 225225, - /* C4[2], coeff of eps^3, polynomial in n of order 2 */ - -8448, 4992, -1144, 225225, - /* C4[2], coeff of eps^2, polynomial in n of order 3 */ - -1440, 4160, -4576, 1716, 225225, - /* C4[3], coeff of eps^5, polynomial in n of order 0 */ - -136, 63063, - /* C4[3], coeff of eps^4, polynomial in n of order 1 */ - 1024, -208, 105105, - /* C4[3], coeff of eps^3, polynomial in n of order 2 */ - 3584, -3328, 1144, 315315, - /* C4[4], coeff of eps^5, polynomial in n of order 0 */ - -128, 135135, - /* C4[4], coeff of eps^4, polynomial in n of order 1 */ - -2560, 832, 405405, - /* C4[5], coeff of eps^5, polynomial in n of order 0 */ - 128, 99099, - }; - int o = 0, k = 0, l, j; - for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ - for (j = nC4 - 1; j >= l; --j) { /* coeff of eps^j */ - int m = nC4 - j - 1; /* order of polynomial in n */ - g->C4x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; - o += m + 2; - } - } -} - -int transit(real lon1, real lon2) { - real lon12; - /* Return 1 or -1 if crossing prime meridian in east or west direction. - * Otherwise return zero. */ - /* Compute lon12 the same way as Geodesic::Inverse. */ - lon1 = AngNormalize(lon1); - lon2 = AngNormalize(lon2); - lon12 = AngDiff(lon1, lon2, 0); - return lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 : - (lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0); -} - -int transitdirect(real lon1, real lon2) { -#if HAVE_C99_MATH - lon1 = remainder(lon1, (real)(720)); - lon2 = remainder(lon2, (real)(720)); - return ( (lon2 <= 0 && lon2 > -360 ? 1 : 0) - - (lon1 <= 0 && lon1 > -360 ? 1 : 0) ); -#else - lon1 = fmod(lon1, (real)(720)); - lon2 = fmod(lon2, (real)(720)); - return ( ((lon2 <= 0 && lon2 > -360) || lon2 > 360 ? 1 : 0) - - ((lon1 <= 0 && lon1 > -360) || lon1 > 360 ? 1 : 0) ); -#endif -} - -void accini(real s[]) { - /* Initialize an accumulator; this is an array with two elements. */ - s[0] = s[1] = 0; -} - -void acccopy(const real s[], real t[]) { - /* Copy an accumulator; t = s. */ - t[0] = s[0]; t[1] = s[1]; -} - -void accadd(real s[], real y) { - /* Add y to an accumulator. */ - real u, z = sumx(y, s[1], &u); - s[0] = sumx(z, s[0], &s[1]); - if (s[0] == 0) - s[0] = u; - else - s[1] = s[1] + u; -} - -real accsum(const real s[], real y) { - /* Return accumulator + y (but don't add to accumulator). */ - real t[2]; - acccopy(s, t); - accadd(t, y); - return t[0]; -} - -void accneg(real s[]) { - /* Negate an accumulator. */ - s[0] = -s[0]; s[1] = -s[1]; -} - -void geod_polygon_init(struct geod_polygon* p, boolx polylinep) { - p->polyline = (polylinep != 0); - geod_polygon_clear(p); -} - -void geod_polygon_clear(struct geod_polygon* p) { - p->lat0 = p->lon0 = p->lat = p->lon = NaN; - accini(p->P); - accini(p->A); - p->num = p->crossings = 0; -} - -void geod_polygon_addpoint(const struct geod_geodesic* g, - struct geod_polygon* p, - real lat, real lon) { - lon = AngNormalize(lon); - if (p->num == 0) { - p->lat0 = p->lat = lat; - p->lon0 = p->lon = lon; - } else { - real s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ - geod_geninverse(g, p->lat, p->lon, lat, lon, - &s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12); - accadd(p->P, s12); - if (!p->polyline) { - accadd(p->A, S12); - p->crossings += transit(p->lon, lon); - } - p->lat = lat; p->lon = lon; - } - ++p->num; -} - -void geod_polygon_addedge(const struct geod_geodesic* g, - struct geod_polygon* p, - real azi, real s) { - if (p->num) { /* Do nothing is num is zero */ - /* Initialize S12 to stop Visual Studio warning. Initialization of lat and - * lon is to make CLang static analyzer happy. */ - real lat = 0, lon = 0, S12 = 0; - geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, - &lat, &lon, 0, - 0, 0, 0, 0, p->polyline ? 0 : &S12); - accadd(p->P, s); - if (!p->polyline) { - accadd(p->A, S12); - p->crossings += transitdirect(p->lon, lon); - } - p->lat = lat; p->lon = lon; - ++p->num; - } -} - -unsigned geod_polygon_compute(const struct geod_geodesic* g, - const struct geod_polygon* p, - boolx reverse, boolx sign, - real* pA, real* pP) { - real s12, S12, t[2], area0; - int crossings; - if (p->num < 2) { - if (pP) *pP = 0; - if (!p->polyline && pA) *pA = 0; - return p->num; - } - if (p->polyline) { - if (pP) *pP = p->P[0]; - return p->num; - } - geod_geninverse(g, p->lat, p->lon, p->lat0, p->lon0, - &s12, 0, 0, 0, 0, 0, &S12); - if (pP) *pP = accsum(p->P, s12); - acccopy(p->A, t); - accadd(t, S12); - crossings = p->crossings + transit(p->lon, p->lon0); - area0 = 4 * pi * g->c2; - if (crossings & 1) - accadd(t, (t[0] < 0 ? 1 : -1) * area0/2); - /* area is with the clockwise sense. If !reverse convert to - * counter-clockwise convention. */ - if (!reverse) - accneg(t); - /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ - if (sign) { - if (t[0] > area0/2) - accadd(t, -area0); - else if (t[0] <= -area0/2) - accadd(t, +area0); - } else { - if (t[0] >= area0) - accadd(t, -area0); - else if (t[0] < 0) - accadd(t, +area0); - } - if (pA) *pA = 0 + t[0]; - return p->num; -} - -unsigned geod_polygon_testpoint(const struct geod_geodesic* g, - const struct geod_polygon* p, - real lat, real lon, - boolx reverse, boolx sign, - real* pA, real* pP) { - real perimeter, tempsum, area0; - int crossings, i; - unsigned num = p->num + 1; - if (num == 1) { - if (pP) *pP = 0; - if (!p->polyline && pA) *pA = 0; - return num; - } - perimeter = p->P[0]; - tempsum = p->polyline ? 0 : p->A[0]; - crossings = p->crossings; - for (i = 0; i < (p->polyline ? 1 : 2); ++i) { - real s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ - geod_geninverse(g, - i == 0 ? p->lat : lat, i == 0 ? p->lon : lon, - i != 0 ? p->lat0 : lat, i != 0 ? p->lon0 : lon, - &s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12); - perimeter += s12; - if (!p->polyline) { - tempsum += S12; - crossings += transit(i == 0 ? p->lon : lon, - i != 0 ? p->lon0 : lon); - } - } - - if (pP) *pP = perimeter; - if (p->polyline) - return num; - - area0 = 4 * pi * g->c2; - if (crossings & 1) - tempsum += (tempsum < 0 ? 1 : -1) * area0/2; - /* area is with the clockwise sense. If !reverse convert to - * counter-clockwise convention. */ - if (!reverse) - tempsum *= -1; - /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ - if (sign) { - if (tempsum > area0/2) - tempsum -= area0; - else if (tempsum <= -area0/2) - tempsum += area0; - } else { - if (tempsum >= area0) - tempsum -= area0; - else if (tempsum < 0) - tempsum += area0; - } - if (pA) *pA = 0 + tempsum; - return num; -} - -unsigned geod_polygon_testedge(const struct geod_geodesic* g, - const struct geod_polygon* p, - real azi, real s, - boolx reverse, boolx sign, - real* pA, real* pP) { - real perimeter, tempsum, area0; - int crossings; - unsigned num = p->num + 1; - if (num == 1) { /* we don't have a starting point! */ - if (pP) *pP = NaN; - if (!p->polyline && pA) *pA = NaN; - return 0; - } - perimeter = p->P[0] + s; - if (p->polyline) { - if (pP) *pP = perimeter; - return num; - } - - tempsum = p->A[0]; - crossings = p->crossings; - { - /* Initialization of lat, lon, and S12 is to make CLang static analyzer - happy. */ - real lat = 0, lon = 0, s12, S12 = 0; - geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, - &lat, &lon, 0, - 0, 0, 0, 0, &S12); - tempsum += S12; - crossings += transitdirect(p->lon, lon); - geod_geninverse(g, lat, lon, p->lat0, p->lon0, - &s12, 0, 0, 0, 0, 0, &S12); - perimeter += s12; - tempsum += S12; - crossings += transit(lon, p->lon0); - } - - area0 = 4 * pi * g->c2; - if (crossings & 1) - tempsum += (tempsum < 0 ? 1 : -1) * area0/2; - /* area is with the clockwise sense. If !reverse convert to - * counter-clockwise convention. */ - if (!reverse) - tempsum *= -1; - /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ - if (sign) { - if (tempsum > area0/2) - tempsum -= area0; - else if (tempsum <= -area0/2) - tempsum += area0; - } else { - if (tempsum >= area0) - tempsum -= area0; - else if (tempsum < 0) - tempsum += area0; - } - if (pP) *pP = perimeter; - if (pA) *pA = 0 + tempsum; - return num; -} - -void geod_polygonarea(const struct geod_geodesic* g, - real lats[], real lons[], int n, - real* pA, real* pP) { - int i; - struct geod_polygon p; - geod_polygon_init(&p, FALSE); - for (i = 0; i < n; ++i) - geod_polygon_addpoint(g, &p, lats[i], lons[i]); - geod_polygon_compute(g, &p, FALSE, TRUE, pA, pP); -} - -/** @endcond */ diff --git a/src/geodesic.cpp b/src/geodesic.cpp new file mode 100644 index 00000000..220dcd7f --- /dev/null +++ b/src/geodesic.cpp @@ -0,0 +1,2100 @@ +/** + * \file geodesic.c + * \brief Implementation of the geodesic routines in C + * + * For the full documentation see geodesic.h. + **********************************************************************/ + +/** @cond SKIP */ + +/* + * This is a C implementation of the geodesic algorithms described in + * + * C. F. F. Karney, + * Algorithms for geodesics, + * J. Geodesy 87, 43--55 (2013); + * https://doi.org/10.1007/s00190-012-0578-z + * Addenda: https://geographiclib.sourceforge.io/geod-addenda.html + * + * See the comments in geodesic.h for documentation. + * + * Copyright (c) Charles Karney (2012-2018) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + */ + +#include "geodesic.h" +#ifdef PJ_LIB__ +#include "proj_math.h" +#else +#include +#endif + +#if !defined(HAVE_C99_MATH) +#define HAVE_C99_MATH 0 +#endif + +#define GEOGRAPHICLIB_GEODESIC_ORDER 6 +#define nA1 GEOGRAPHICLIB_GEODESIC_ORDER +#define nC1 GEOGRAPHICLIB_GEODESIC_ORDER +#define nC1p GEOGRAPHICLIB_GEODESIC_ORDER +#define nA2 GEOGRAPHICLIB_GEODESIC_ORDER +#define nC2 GEOGRAPHICLIB_GEODESIC_ORDER +#define nA3 GEOGRAPHICLIB_GEODESIC_ORDER +#define nA3x nA3 +#define nC3 GEOGRAPHICLIB_GEODESIC_ORDER +#define nC3x ((nC3 * (nC3 - 1)) / 2) +#define nC4 GEOGRAPHICLIB_GEODESIC_ORDER +#define nC4x ((nC4 * (nC4 + 1)) / 2) +#define nC (GEOGRAPHICLIB_GEODESIC_ORDER + 1) + +typedef double real; +typedef int boolx; + +static unsigned init = 0; +static const int FALSE = 0; +static const int TRUE = 1; +static unsigned digits, maxit1, maxit2; +static real epsilon, realmin, pi, degree, NaN, + tiny, tol0, tol1, tol2, tolb, xthresh; + +static void Init() { + if (!init) { +#if defined(__DBL_MANT_DIG__) + digits = __DBL_MANT_DIG__; +#else + digits = 53; +#endif +#if defined(__DBL_EPSILON__) + epsilon = __DBL_EPSILON__; +#else + epsilon = pow(0.5, digits - 1); +#endif +#if defined(__DBL_MIN__) + realmin = __DBL_MIN__; +#else + realmin = pow(0.5, 1022); +#endif +#if defined(M_PI) + pi = M_PI; +#else + pi = atan2(0.0, -1.0); +#endif + maxit1 = 20; + maxit2 = maxit1 + digits + 10; + tiny = sqrt(realmin); + tol0 = epsilon; + /* Increase multiplier in defn of tol1 from 100 to 200 to fix inverse case + * 52.784459512564 0 -52.784459512563990912 179.634407464943777557 + * which otherwise failed for Visual Studio 10 (Release and Debug) */ + tol1 = 200 * tol0; + tol2 = sqrt(tol0); + /* Check on bisection interval */ + tolb = tol0 * tol2; + xthresh = 1000 * tol2; + degree = pi/180; + #if defined(NAN) + NaN = NAN; + #else + { + real minus1 = -1; + /* cppcheck-suppress wrongmathcall */ + NaN = sqrt(minus1); + } + #endif + init = 1; + } +} + +enum captype { + CAP_NONE = 0U, + CAP_C1 = 1U<<0, + CAP_C1p = 1U<<1, + CAP_C2 = 1U<<2, + CAP_C3 = 1U<<3, + CAP_C4 = 1U<<4, + CAP_ALL = 0x1FU, + OUT_ALL = 0x7F80U +}; + +static real sq(real x) { return x * x; } +#if HAVE_C99_MATH +#define atanhx atanh +#define copysignx copysign +#define hypotx hypot +#define cbrtx cbrt +#else +static real log1px(real x) { + volatile real + y = 1 + x, + z = y - 1; + /* Here's the explanation for this magic: y = 1 + z, exactly, and z + * approx x, thus log(y)/z (which is nearly constant near z = 0) returns + * a good approximation to the true log(1 + x)/x. The multiplication x * + * (log(y)/z) introduces little additional error. */ + return z == 0 ? x : x * log(y) / z; +} + +static real atanhx(real x) { + real y = fabs(x); /* Enforce odd parity */ + y = log1px(2 * y/(1 - y))/2; + return x < 0 ? -y : y; +} + +static real copysignx(real x, real y) { + return fabs(x) * (y < 0 || (y == 0 && 1/y < 0) ? -1 : 1); +} + +static real hypotx(real x, real y) +{ return sqrt(x * x + y * y); } + +static real cbrtx(real x) { + real y = pow(fabs(x), 1/(real)(3)); /* Return the real cube root */ + return x < 0 ? -y : y; +} +#endif + +static real sumx(real u, real v, real* t) { + volatile real s = u + v; + volatile real up = s - v; + volatile real vpp = s - up; + up -= u; + vpp -= v; + if (t) *t = -(up + vpp); + /* error-free sum: + * u + v = s + t + * = round(u + v) + t */ + return s; +} + +static real polyval(int N, const real p[], real x) { + real y = N < 0 ? 0 : *p++; + while (--N >= 0) y = y * x + *p++; + return y; +} + +/* mimic C++ std::min and std::max */ +static real minx(real a, real b) +{ return (b < a) ? b : a; } + +static real maxx(real a, real b) +{ return (a < b) ? b : a; } + +static void swapx(real* x, real* y) +{ real t = *x; *x = *y; *y = t; } + +static void norm2(real* sinx, real* cosx) { + real r = hypotx(*sinx, *cosx); + *sinx /= r; + *cosx /= r; +} + +static real AngNormalize(real x) { +#if HAVE_C99_MATH + x = remainder(x, (real)(360)); + return x != -180 ? x : 180; +#else + real y = fmod(x, (real)(360)); +#if defined(_MSC_VER) && _MSC_VER < 1900 + /* + * Before version 14 (2015), Visual Studio had problems dealing + * with -0.0. Specifically + * VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 + * sincosdx has a similar fix. + * python 2.7 on Windows 32-bit machines has the same problem. + */ + if (x == 0) y = x; +#endif + return y <= -180 ? y + 360 : (y <= 180 ? y : y - 360); +#endif +} + +static real LatFix(real x) +{ return fabs(x) > 90 ? NaN : x; } + +static real AngDiff(real x, real y, real* e) { + real t, d = AngNormalize(sumx(AngNormalize(-x), AngNormalize(y), &t)); + /* Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and + * abs(t) <= eps (eps = 2^-45 for doubles). The only case where the + * addition of t takes the result outside the range (-180,180] is d = 180 + * and t > 0. The case, d = -180 + eps, t = -eps, can't happen, since + * sum would have returned the exact result in such a case (i.e., given t + * = 0). */ + return sumx(d == 180 && t > 0 ? -180 : d, t, e); +} + +static real AngRound(real x) { + const real z = 1/(real)(16); + volatile real y; + if (x == 0) return 0; + y = fabs(x); + /* The compiler mustn't "simplify" z - (z - y) to y */ + y = y < z ? z - (z - y) : y; + return x < 0 ? -y : y; +} + +static void sincosdx(real x, real* sinx, real* cosx) { + /* In order to minimize round-off errors, this function exactly reduces + * the argument to the range [-45, 45] before converting it to radians. */ + real r, s, c; int q; +#if HAVE_C99_MATH && !defined(__GNUC__) + /* Disable for gcc because of bug in glibc version < 2.22, see + * https://sourceware.org/bugzilla/show_bug.cgi?id=17569 */ + r = remquo(x, (real)(90), &q); +#else + r = fmod(x, (real)(360)); + /* check for NaN */ + q = r == r ? (int)(floor(r / 90 + (real)(0.5))) : 0; + r -= 90 * q; +#endif + /* now abs(r) <= 45 */ + r *= degree; + /* Possibly could call the gnu extension sincos */ + s = sin(r); c = cos(r); +#if defined(_MSC_VER) && _MSC_VER < 1900 + /* + * Before version 14 (2015), Visual Studio had problems dealing + * with -0.0. Specifically + * VC 10,11,12 and 32-bit compile: fmod(-0.0, 360.0) -> +0.0 + * VC 12 and 64-bit compile: sin(-0.0) -> +0.0 + * AngNormalize has a similar fix. + * python 2.7 on Windows 32-bit machines has the same problem. + */ + if (x == 0) s = x; +#endif + switch ((unsigned)q & 3U) { + case 0U: *sinx = s; *cosx = c; break; + case 1U: *sinx = c; *cosx = -s; break; + case 2U: *sinx = -s; *cosx = -c; break; + default: *sinx = -c; *cosx = s; break; /* case 3U */ + } + if (x != 0) { *sinx += (real)(0); *cosx += (real)(0); } +} + +static real atan2dx(real y, real x) { + /* In order to minimize round-off errors, this function rearranges the + * arguments so that result of atan2 is in the range [-pi/4, pi/4] before + * converting it to degrees and mapping the result to the correct + * quadrant. */ + int q = 0; real ang; + if (fabs(y) > fabs(x)) { swapx(&x, &y); q = 2; } + if (x < 0) { x = -x; ++q; } + /* here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4] */ + ang = atan2(y, x) / degree; + switch (q) { + /* Note that atan2d(-0.0, 1.0) will return -0. However, we expect that + * atan2d will not be called with y = -0. If need be, include + * + * case 0: ang = 0 + ang; break; + */ + case 1: ang = (y >= 0 ? 180 : -180) - ang; break; + case 2: ang = 90 - ang; break; + case 3: ang = -90 + ang; break; + } + return ang; +} + +static void A3coeff(struct geod_geodesic* g); +static void C3coeff(struct geod_geodesic* g); +static void C4coeff(struct geod_geodesic* g); +static real SinCosSeries(boolx sinp, + real sinx, real cosx, + const real c[], int n); +static void Lengths(const struct geod_geodesic* g, + real eps, real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, + real* ps12b, real* pm12b, real* pm0, + real* pM12, real* pM21, + /* Scratch area of the right size */ + real Ca[]); +static real Astroid(real x, real y); +static real InverseStart(const struct geod_geodesic* g, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real* psalp1, real* pcalp1, + /* Only updated if return val >= 0 */ + real* psalp2, real* pcalp2, + /* Only updated for short lines */ + real* pdnm, + /* Scratch area of the right size */ + real Ca[]); +static real Lambda12(const struct geod_geodesic* g, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, + real slam120, real clam120, + real* psalp2, real* pcalp2, + real* psig12, + real* pssig1, real* pcsig1, + real* pssig2, real* pcsig2, + real* peps, + real* pdomg12, + boolx diffp, real* pdlam12, + /* Scratch area of the right size */ + real Ca[]); +static real A3f(const struct geod_geodesic* g, real eps); +static void C3f(const struct geod_geodesic* g, real eps, real c[]); +static void C4f(const struct geod_geodesic* g, real eps, real c[]); +static real A1m1f(real eps); +static void C1f(real eps, real c[]); +static void C1pf(real eps, real c[]); +static real A2m1f(real eps); +static void C2f(real eps, real c[]); +static int transit(real lon1, real lon2); +static int transitdirect(real lon1, real lon2); +static void accini(real s[]); +static void acccopy(const real s[], real t[]); +static void accadd(real s[], real y); +static real accsum(const real s[], real y); +static void accneg(real s[]); + +void geod_init(struct geod_geodesic* g, real a, real f) { + if (!init) Init(); + g->a = a; + g->f = f; + g->f1 = 1 - g->f; + g->e2 = g->f * (2 - g->f); + g->ep2 = g->e2 / sq(g->f1); /* e2 / (1 - e2) */ + g->n = g->f / ( 2 - g->f); + g->b = g->a * g->f1; + g->c2 = (sq(g->a) + sq(g->b) * + (g->e2 == 0 ? 1 : + (g->e2 > 0 ? atanhx(sqrt(g->e2)) : atan(sqrt(-g->e2))) / + sqrt(fabs(g->e2))))/2; /* authalic radius squared */ + /* The sig12 threshold for "really short". Using the auxiliary sphere + * solution with dnm computed at (bet1 + bet2) / 2, the relative error in the + * azimuth consistency check is sig12^2 * abs(f) * min(1, 1-f/2) / 2. (Error + * measured for 1/100 < b/a < 100 and abs(f) >= 1/1000. For a given f and + * sig12, the max error occurs for lines near the pole. If the old rule for + * computing dnm = (dn1 + dn2)/2 is used, then the error increases by a + * factor of 2.) Setting this equal to epsilon gives sig12 = etol2. Here + * 0.1 is a safety factor (error decreased by 100) and max(0.001, abs(f)) + * stops etol2 getting too large in the nearly spherical case. */ + g->etol2 = 0.1 * tol2 / + sqrt( maxx((real)(0.001), fabs(g->f)) * minx((real)(1), 1 - g->f/2) / 2 ); + + A3coeff(g); + C3coeff(g); + C4coeff(g); +} + +static void geod_lineinit_int(struct geod_geodesicline* l, + const struct geod_geodesic* g, + real lat1, real lon1, + real azi1, real salp1, real calp1, + unsigned caps) { + real cbet1, sbet1, eps; + l->a = g->a; + l->f = g->f; + l->b = g->b; + l->c2 = g->c2; + l->f1 = g->f1; + /* If caps is 0 assume the standard direct calculation */ + l->caps = (caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE) | + /* always allow latitude and azimuth and unrolling of longitude */ + GEOD_LATITUDE | GEOD_AZIMUTH | GEOD_LONG_UNROLL; + + l->lat1 = LatFix(lat1); + l->lon1 = lon1; + l->azi1 = azi1; + l->salp1 = salp1; + l->calp1 = calp1; + + sincosdx(AngRound(l->lat1), &sbet1, &cbet1); sbet1 *= l->f1; + /* Ensure cbet1 = +epsilon at poles */ + norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1); + l->dn1 = sqrt(1 + g->ep2 * sq(sbet1)); + + /* Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), */ + l->salp0 = l->salp1 * cbet1; /* alp0 in [0, pi/2 - |bet1|] */ + /* Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following + * is slightly better (consider the case salp1 = 0). */ + l->calp0 = hypotx(l->calp1, l->salp1 * sbet1); + /* Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1). + * sig = 0 is nearest northward crossing of equator. + * With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line). + * With bet1 = pi/2, alp1 = -pi, sig1 = pi/2 + * With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2 + * Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1). + * With alp0 in (0, pi/2], quadrants for sig and omg coincide. + * No atan2(0,0) ambiguity at poles since cbet1 = +epsilon. + * With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi. */ + l->ssig1 = sbet1; l->somg1 = l->salp0 * sbet1; + l->csig1 = l->comg1 = sbet1 != 0 || l->calp1 != 0 ? cbet1 * l->calp1 : 1; + norm2(&l->ssig1, &l->csig1); /* sig1 in (-pi, pi] */ + /* norm2(somg1, comg1); -- don't need to normalize! */ + + l->k2 = sq(l->calp0) * g->ep2; + eps = l->k2 / (2 * (1 + sqrt(1 + l->k2)) + l->k2); + + if (l->caps & CAP_C1) { + real s, c; + l->A1m1 = A1m1f(eps); + C1f(eps, l->C1a); + l->B11 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C1a, nC1); + s = sin(l->B11); c = cos(l->B11); + /* tau1 = sig1 + B11 */ + l->stau1 = l->ssig1 * c + l->csig1 * s; + l->ctau1 = l->csig1 * c - l->ssig1 * s; + /* Not necessary because C1pa reverts C1a + * B11 = -SinCosSeries(TRUE, stau1, ctau1, C1pa, nC1p); */ + } + + if (l->caps & CAP_C1p) + C1pf(eps, l->C1pa); + + if (l->caps & CAP_C2) { + l->A2m1 = A2m1f(eps); + C2f(eps, l->C2a); + l->B21 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C2a, nC2); + } + + if (l->caps & CAP_C3) { + C3f(g, eps, l->C3a); + l->A3c = -l->f * l->salp0 * A3f(g, eps); + l->B31 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C3a, nC3-1); + } + + if (l->caps & CAP_C4) { + C4f(g, eps, l->C4a); + /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) */ + l->A4 = sq(l->a) * l->calp0 * l->salp0 * g->e2; + l->B41 = SinCosSeries(FALSE, l->ssig1, l->csig1, l->C4a, nC4); + } + + l->a13 = l->s13 = NaN; +} + +void geod_lineinit(struct geod_geodesicline* l, + const struct geod_geodesic* g, + real lat1, real lon1, real azi1, unsigned caps) { + real salp1, calp1; + azi1 = AngNormalize(azi1); + /* Guard against underflow in salp0 */ + sincosdx(AngRound(azi1), &salp1, &calp1); + geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); +} + +void geod_gendirectline(struct geod_geodesicline* l, + const struct geod_geodesic* g, + real lat1, real lon1, real azi1, + unsigned flags, real s12_a12, + unsigned caps) { + geod_lineinit(l, g, lat1, lon1, azi1, caps); + geod_gensetdistance(l, flags, s12_a12); +} + +void geod_directline(struct geod_geodesicline* l, + const struct geod_geodesic* g, + real lat1, real lon1, real azi1, + real s12, unsigned caps) { + geod_gendirectline(l, g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, caps); +} + +real geod_genposition(const struct geod_geodesicline* l, + unsigned flags, real s12_a12, + real* plat2, real* plon2, real* pazi2, + real* ps12, real* pm12, + real* pM12, real* pM21, + real* pS12) { + real lat2 = 0, lon2 = 0, azi2 = 0, s12 = 0, + m12 = 0, M12 = 0, M21 = 0, S12 = 0; + /* Avoid warning about uninitialized B12. */ + real sig12, ssig12, csig12, B12 = 0, AB1 = 0; + real omg12, lam12, lon12; + real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2; + unsigned outmask = + (plat2 ? GEOD_LATITUDE : 0U) | + (plon2 ? GEOD_LONGITUDE : 0U) | + (pazi2 ? GEOD_AZIMUTH : 0U) | + (ps12 ? GEOD_DISTANCE : 0U) | + (pm12 ? GEOD_REDUCEDLENGTH : 0U) | + (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | + (pS12 ? GEOD_AREA : 0U); + + outmask &= l->caps & OUT_ALL; + if (!( TRUE /*Init()*/ && + (flags & GEOD_ARCMODE || (l->caps & (GEOD_DISTANCE_IN & OUT_ALL))) )) + /* Uninitialized or impossible distance calculation requested */ + return NaN; + + if (flags & GEOD_ARCMODE) { + /* Interpret s12_a12 as spherical arc length */ + sig12 = s12_a12 * degree; + sincosdx(s12_a12, &ssig12, &csig12); + } else { + /* Interpret s12_a12 as distance */ + real + tau12 = s12_a12 / (l->b * (1 + l->A1m1)), + s = sin(tau12), + c = cos(tau12); + /* tau2 = tau1 + tau12 */ + B12 = - SinCosSeries(TRUE, + l->stau1 * c + l->ctau1 * s, + l->ctau1 * c - l->stau1 * s, + l->C1pa, nC1p); + sig12 = tau12 - (B12 - l->B11); + ssig12 = sin(sig12); csig12 = cos(sig12); + if (fabs(l->f) > 0.01) { + /* Reverted distance series is inaccurate for |f| > 1/100, so correct + * sig12 with 1 Newton iteration. The following table shows the + * approximate maximum error for a = WGS_a() and various f relative to + * GeodesicExact. + * erri = the error in the inverse solution (nm) + * errd = the error in the direct solution (series only) (nm) + * errda = the error in the direct solution (series + 1 Newton) (nm) + * + * f erri errd errda + * -1/5 12e6 1.2e9 69e6 + * -1/10 123e3 12e6 765e3 + * -1/20 1110 108e3 7155 + * -1/50 18.63 200.9 27.12 + * -1/100 18.63 23.78 23.37 + * -1/150 18.63 21.05 20.26 + * 1/150 22.35 24.73 25.83 + * 1/100 22.35 25.03 25.31 + * 1/50 29.80 231.9 30.44 + * 1/20 5376 146e3 10e3 + * 1/10 829e3 22e6 1.5e6 + * 1/5 157e6 3.8e9 280e6 */ + real serr; + ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; + csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; + B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); + serr = (1 + l->A1m1) * (sig12 + (B12 - l->B11)) - s12_a12 / l->b; + sig12 = sig12 - serr / sqrt(1 + l->k2 * sq(ssig2)); + ssig12 = sin(sig12); csig12 = cos(sig12); + /* Update B12 below */ + } + } + + /* sig2 = sig1 + sig12 */ + ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; + csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; + dn2 = sqrt(1 + l->k2 * sq(ssig2)); + if (outmask & (GEOD_DISTANCE | GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { + if (flags & GEOD_ARCMODE || fabs(l->f) > 0.01) + B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); + AB1 = (1 + l->A1m1) * (B12 - l->B11); + } + /* sin(bet2) = cos(alp0) * sin(sig2) */ + sbet2 = l->calp0 * ssig2; + /* Alt: cbet2 = hypot(csig2, salp0 * ssig2); */ + cbet2 = hypotx(l->salp0, l->calp0 * csig2); + if (cbet2 == 0) + /* I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case */ + cbet2 = csig2 = tiny; + /* tan(alp0) = cos(sig2)*tan(alp2) */ + salp2 = l->salp0; calp2 = l->calp0 * csig2; /* No need to normalize */ + + if (outmask & GEOD_DISTANCE) + s12 = (flags & GEOD_ARCMODE) ? + l->b * ((1 + l->A1m1) * sig12 + AB1) : + s12_a12; + + if (outmask & GEOD_LONGITUDE) { + real E = copysignx(1, l->salp0); /* east or west going? */ + /* tan(omg2) = sin(alp0) * tan(sig2) */ + somg2 = l->salp0 * ssig2; comg2 = csig2; /* No need to normalize */ + /* omg12 = omg2 - omg1 */ + omg12 = (flags & GEOD_LONG_UNROLL) + ? E * (sig12 + - (atan2( ssig2, csig2) - atan2( l->ssig1, l->csig1)) + + (atan2(E * somg2, comg2) - atan2(E * l->somg1, l->comg1))) + : atan2(somg2 * l->comg1 - comg2 * l->somg1, + comg2 * l->comg1 + somg2 * l->somg1); + lam12 = omg12 + l->A3c * + ( sig12 + (SinCosSeries(TRUE, ssig2, csig2, l->C3a, nC3-1) + - l->B31)); + lon12 = lam12 / degree; + lon2 = (flags & GEOD_LONG_UNROLL) ? l->lon1 + lon12 : + AngNormalize(AngNormalize(l->lon1) + AngNormalize(lon12)); + } + + if (outmask & GEOD_LATITUDE) + lat2 = atan2dx(sbet2, l->f1 * cbet2); + + if (outmask & GEOD_AZIMUTH) + azi2 = atan2dx(salp2, calp2); + + if (outmask & (GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { + real + B22 = SinCosSeries(TRUE, ssig2, csig2, l->C2a, nC2), + AB2 = (1 + l->A2m1) * (B22 - l->B21), + J12 = (l->A1m1 - l->A2m1) * sig12 + (AB1 - AB2); + if (outmask & GEOD_REDUCEDLENGTH) + /* Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure + * accurate cancellation in the case of coincident points. */ + m12 = l->b * ((dn2 * (l->csig1 * ssig2) - l->dn1 * (l->ssig1 * csig2)) + - l->csig1 * csig2 * J12); + if (outmask & GEOD_GEODESICSCALE) { + real t = l->k2 * (ssig2 - l->ssig1) * (ssig2 + l->ssig1) / + (l->dn1 + dn2); + M12 = csig12 + (t * ssig2 - csig2 * J12) * l->ssig1 / l->dn1; + M21 = csig12 - (t * l->ssig1 - l->csig1 * J12) * ssig2 / dn2; + } + } + + if (outmask & GEOD_AREA) { + real + B42 = SinCosSeries(FALSE, ssig2, csig2, l->C4a, nC4); + real salp12, calp12; + if (l->calp0 == 0 || l->salp0 == 0) { + /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ + salp12 = salp2 * l->calp1 - calp2 * l->salp1; + calp12 = calp2 * l->calp1 + salp2 * l->salp1; + } else { + /* tan(alp) = tan(alp0) * sec(sig) + * tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1) + * = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2) + * If csig12 > 0, write + * csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1) + * else + * csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1 + * No need to normalize */ + salp12 = l->calp0 * l->salp0 * + (csig12 <= 0 ? l->csig1 * (1 - csig12) + ssig12 * l->ssig1 : + ssig12 * (l->csig1 * ssig12 / (1 + csig12) + l->ssig1)); + calp12 = sq(l->salp0) + sq(l->calp0) * l->csig1 * csig2; + } + S12 = l->c2 * atan2(salp12, calp12) + l->A4 * (B42 - l->B41); + } + + /* In the pattern + * + * if ((outmask & GEOD_XX) && pYY) + * *pYY = YY; + * + * the second check "&& pYY" is redundant. It's there to make the CLang + * static analyzer happy. + */ + if ((outmask & GEOD_LATITUDE) && plat2) + *plat2 = lat2; + if ((outmask & GEOD_LONGITUDE) && plon2) + *plon2 = lon2; + if ((outmask & GEOD_AZIMUTH) && pazi2) + *pazi2 = azi2; + if ((outmask & GEOD_DISTANCE) && ps12) + *ps12 = s12; + if ((outmask & GEOD_REDUCEDLENGTH) && pm12) + *pm12 = m12; + if (outmask & GEOD_GEODESICSCALE) { + if (pM12) *pM12 = M12; + if (pM21) *pM21 = M21; + } + if ((outmask & GEOD_AREA) && pS12) + *pS12 = S12; + + return (flags & GEOD_ARCMODE) ? s12_a12 : sig12 / degree; +} + +void geod_setdistance(struct geod_geodesicline* l, real s13) { + l->s13 = s13; + l->a13 = geod_genposition(l, GEOD_NOFLAGS, l->s13, 0, 0, 0, 0, 0, 0, 0, 0); +} + +static void geod_setarc(struct geod_geodesicline* l, real a13) { + l->a13 = a13; l->s13 = NaN; + geod_genposition(l, GEOD_ARCMODE, l->a13, 0, 0, 0, &l->s13, 0, 0, 0, 0); +} + +void geod_gensetdistance(struct geod_geodesicline* l, + unsigned flags, real s13_a13) { + (flags & GEOD_ARCMODE) ? + geod_setarc(l, s13_a13) : + geod_setdistance(l, s13_a13); +} + +void geod_position(const struct geod_geodesicline* l, real s12, + real* plat2, real* plon2, real* pazi2) { + geod_genposition(l, FALSE, s12, plat2, plon2, pazi2, 0, 0, 0, 0, 0); +} + +real geod_gendirect(const struct geod_geodesic* g, + real lat1, real lon1, real azi1, + unsigned flags, real s12_a12, + real* plat2, real* plon2, real* pazi2, + real* ps12, real* pm12, real* pM12, real* pM21, + real* pS12) { + struct geod_geodesicline l; + unsigned outmask = + (plat2 ? GEOD_LATITUDE : 0U) | + (plon2 ? GEOD_LONGITUDE : 0U) | + (pazi2 ? GEOD_AZIMUTH : 0U) | + (ps12 ? GEOD_DISTANCE : 0U) | + (pm12 ? GEOD_REDUCEDLENGTH : 0U) | + (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | + (pS12 ? GEOD_AREA : 0U); + + geod_lineinit(&l, g, lat1, lon1, azi1, + /* Automatically supply GEOD_DISTANCE_IN if necessary */ + outmask | + ((flags & GEOD_ARCMODE) ? GEOD_NONE : GEOD_DISTANCE_IN)); + return geod_genposition(&l, flags, s12_a12, + plat2, plon2, pazi2, ps12, pm12, pM12, pM21, pS12); +} + +void geod_direct(const struct geod_geodesic* g, + real lat1, real lon1, real azi1, + real s12, + real* plat2, real* plon2, real* pazi2) { + geod_gendirect(g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, plat2, plon2, pazi2, + 0, 0, 0, 0, 0); +} + +static real geod_geninverse_int(const struct geod_geodesic* g, + real lat1, real lon1, real lat2, real lon2, + real* ps12, + real* psalp1, real* pcalp1, + real* psalp2, real* pcalp2, + real* pm12, real* pM12, real* pM21, + real* pS12) { + real s12 = 0, m12 = 0, M12 = 0, M21 = 0, S12 = 0; + real lon12, lon12s; + int latsign, lonsign, swapp; + real sbet1, cbet1, sbet2, cbet2, s12x = 0, m12x = 0; + real dn1, dn2, lam12, slam12, clam12; + real a12 = 0, sig12, calp1 = 0, salp1 = 0, calp2 = 0, salp2 = 0; + real Ca[nC]; + boolx meridian; + /* somg12 > 1 marks that it needs to be calculated */ + real omg12 = 0, somg12 = 2, comg12 = 0; + + unsigned outmask = + (ps12 ? GEOD_DISTANCE : 0U) | + (pm12 ? GEOD_REDUCEDLENGTH : 0U) | + (pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) | + (pS12 ? GEOD_AREA : 0U); + + outmask &= OUT_ALL; + /* Compute longitude difference (AngDiff does this carefully). Result is + * in [-180, 180] but -180 is only for west-going geodesics. 180 is for + * east-going and meridional geodesics. */ + lon12 = AngDiff(lon1, lon2, &lon12s); + /* Make longitude difference positive. */ + lonsign = lon12 >= 0 ? 1 : -1; + /* If very close to being on the same half-meridian, then make it so. */ + lon12 = lonsign * AngRound(lon12); + lon12s = AngRound((180 - lon12) - lonsign * lon12s); + lam12 = lon12 * degree; + if (lon12 > 90) { + sincosdx(lon12s, &slam12, &clam12); + clam12 = -clam12; + } else + sincosdx(lon12, &slam12, &clam12); + + /* If really close to the equator, treat as on equator. */ + lat1 = AngRound(LatFix(lat1)); + lat2 = AngRound(LatFix(lat2)); + /* Swap points so that point with higher (abs) latitude is point 1 + * If one latitude is a nan, then it becomes lat1. */ + swapp = fabs(lat1) < fabs(lat2) ? -1 : 1; + if (swapp < 0) { + lonsign *= -1; + swapx(&lat1, &lat2); + } + /* Make lat1 <= 0 */ + latsign = lat1 < 0 ? 1 : -1; + lat1 *= latsign; + lat2 *= latsign; + /* Now we have + * + * 0 <= lon12 <= 180 + * -90 <= lat1 <= 0 + * lat1 <= lat2 <= -lat1 + * + * longsign, swapp, latsign register the transformation to bring the + * coordinates to this canonical form. In all cases, 1 means no change was + * made. We make these transformations so that there are few cases to + * check, e.g., on verifying quadrants in atan2. In addition, this + * enforces some symmetries in the results returned. */ + + sincosdx(lat1, &sbet1, &cbet1); sbet1 *= g->f1; + /* Ensure cbet1 = +epsilon at poles */ + norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1); + + sincosdx(lat2, &sbet2, &cbet2); sbet2 *= g->f1; + /* Ensure cbet2 = +epsilon at poles */ + norm2(&sbet2, &cbet2); cbet2 = maxx(tiny, cbet2); + + /* If cbet1 < -sbet1, then cbet2 - cbet1 is a sensitive measure of the + * |bet1| - |bet2|. Alternatively (cbet1 >= -sbet1), abs(sbet2) + sbet1 is + * a better measure. This logic is used in assigning calp2 in Lambda12. + * Sometimes these quantities vanish and in that case we force bet2 = +/- + * bet1 exactly. An example where is is necessary is the inverse problem + * 48.522876735459 0 -48.52287673545898293 179.599720456223079643 + * which failed with Visual Studio 10 (Release and Debug) */ + + if (cbet1 < -sbet1) { + if (cbet2 == cbet1) + sbet2 = sbet2 < 0 ? sbet1 : -sbet1; + } else { + if (fabs(sbet2) == -sbet1) + cbet2 = cbet1; + } + + dn1 = sqrt(1 + g->ep2 * sq(sbet1)); + dn2 = sqrt(1 + g->ep2 * sq(sbet2)); + + meridian = lat1 == -90 || slam12 == 0; + + if (meridian) { + + /* Endpoints are on a single full meridian, so the geodesic might lie on + * a meridian. */ + + real ssig1, csig1, ssig2, csig2; + calp1 = clam12; salp1 = slam12; /* Head to the target longitude */ + calp2 = 1; salp2 = 0; /* At the target we're heading north */ + + /* tan(bet) = tan(sig) * cos(alp) */ + ssig1 = sbet1; csig1 = calp1 * cbet1; + ssig2 = sbet2; csig2 = calp2 * cbet2; + + /* sig12 = sig2 - sig1 */ + sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, &s12x, &m12x, 0, + (outmask & GEOD_GEODESICSCALE) ? &M12 : 0, + (outmask & GEOD_GEODESICSCALE) ? &M21 : 0, + Ca); + /* Add the check for sig12 since zero length geodesics might yield m12 < + * 0. Test case was + * + * echo 20.001 0 20.001 0 | GeodSolve -i + * + * In fact, we will have sig12 > pi/2 for meridional geodesic which is + * not a shortest path. */ + if (sig12 < 1 || m12x >= 0) { + /* Need at least 2, to handle 90 0 90 180 */ + if (sig12 < 3 * tiny) + sig12 = m12x = s12x = 0; + m12x *= g->b; + s12x *= g->b; + a12 = sig12 / degree; + } else + /* m12 < 0, i.e., prolate and too close to anti-podal */ + meridian = FALSE; + } + + if (!meridian && + sbet1 == 0 && /* and sbet2 == 0 */ + /* Mimic the way Lambda12 works with calp1 = 0 */ + (g->f <= 0 || lon12s >= g->f * 180)) { + + /* Geodesic runs along equator */ + calp1 = calp2 = 0; salp1 = salp2 = 1; + s12x = g->a * lam12; + sig12 = omg12 = lam12 / g->f1; + m12x = g->b * sin(sig12); + if (outmask & GEOD_GEODESICSCALE) + M12 = M21 = cos(sig12); + a12 = lon12 / g->f1; + + } else if (!meridian) { + + /* Now point1 and point2 belong within a hemisphere bounded by a + * meridian and geodesic is neither meridional or equatorial. */ + + /* Figure a starting point for Newton's method */ + real dnm = 0; + sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, + lam12, slam12, clam12, + &salp1, &calp1, &salp2, &calp2, &dnm, + Ca); + + if (sig12 >= 0) { + /* Short lines (InverseStart sets salp2, calp2, dnm) */ + s12x = sig12 * g->b * dnm; + m12x = sq(dnm) * g->b * sin(sig12 / dnm); + if (outmask & GEOD_GEODESICSCALE) + M12 = M21 = cos(sig12 / dnm); + a12 = sig12 / degree; + omg12 = lam12 / (g->f1 * dnm); + } else { + + /* Newton's method. This is a straightforward solution of f(alp1) = + * lambda12(alp1) - lam12 = 0 with one wrinkle. f(alp) has exactly one + * root in the interval (0, pi) and its derivative is positive at the + * root. Thus f(alp) is positive for alp > alp1 and negative for alp < + * alp1. During the course of the iteration, a range (alp1a, alp1b) is + * maintained which brackets the root and with each evaluation of + * f(alp) the range is shrunk, if possible. Newton's method is + * restarted whenever the derivative of f is negative (because the new + * value of alp1 is then further from the solution) or if the new + * estimate of alp1 lies outside (0,pi); in this case, the new starting + * guess is taken to be (alp1a + alp1b) / 2. */ + real ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, domg12 = 0; + unsigned numit = 0; + /* Bracketing range */ + real salp1a = tiny, calp1a = 1, salp1b = tiny, calp1b = -1; + boolx tripn = FALSE; + boolx tripb = FALSE; + for (; numit < maxit2; ++numit) { + /* the WGS84 test set: mean = 1.47, sd = 1.25, max = 16 + * WGS84 and random input: mean = 2.85, sd = 0.60 */ + real dv = 0, + v = Lambda12(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1, + slam12, clam12, + &salp2, &calp2, &sig12, &ssig1, &csig1, &ssig2, &csig2, + &eps, &domg12, numit < maxit1, &dv, Ca); + /* 2 * tol0 is approximately 1 ulp for a number in [0, pi]. */ + /* Reversed test to allow escape with NaNs */ + if (tripb || !(fabs(v) >= (tripn ? 8 : 1) * tol0)) break; + /* Update bracketing values */ + if (v > 0 && (numit > maxit1 || calp1/salp1 > calp1b/salp1b)) + { salp1b = salp1; calp1b = calp1; } + else if (v < 0 && (numit > maxit1 || calp1/salp1 < calp1a/salp1a)) + { salp1a = salp1; calp1a = calp1; } + if (numit < maxit1 && dv > 0) { + real + dalp1 = -v/dv; + real + sdalp1 = sin(dalp1), cdalp1 = cos(dalp1), + nsalp1 = salp1 * cdalp1 + calp1 * sdalp1; + if (nsalp1 > 0 && fabs(dalp1) < pi) { + calp1 = calp1 * cdalp1 - salp1 * sdalp1; + salp1 = nsalp1; + norm2(&salp1, &calp1); + /* In some regimes we don't get quadratic convergence because + * slope -> 0. So use convergence conditions based on epsilon + * instead of sqrt(epsilon). */ + tripn = fabs(v) <= 16 * tol0; + continue; + } + } + /* Either dv was not positive or updated value was outside legal + * range. Use the midpoint of the bracket as the next estimate. + * This mechanism is not needed for the WGS84 ellipsoid, but it does + * catch problems with more eccentric ellipsoids. Its efficacy is + * such for the WGS84 test set with the starting guess set to alp1 = + * 90deg: + * the WGS84 test set: mean = 5.21, sd = 3.93, max = 24 + * WGS84 and random input: mean = 4.74, sd = 0.99 */ + salp1 = (salp1a + salp1b)/2; + calp1 = (calp1a + calp1b)/2; + norm2(&salp1, &calp1); + tripn = FALSE; + tripb = (fabs(salp1a - salp1) + (calp1a - calp1) < tolb || + fabs(salp1 - salp1b) + (calp1 - calp1b) < tolb); + } + Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, &s12x, &m12x, 0, + (outmask & GEOD_GEODESICSCALE) ? &M12 : 0, + (outmask & GEOD_GEODESICSCALE) ? &M21 : 0, Ca); + m12x *= g->b; + s12x *= g->b; + a12 = sig12 / degree; + if (outmask & GEOD_AREA) { + /* omg12 = lam12 - domg12 */ + real sdomg12 = sin(domg12), cdomg12 = cos(domg12); + somg12 = slam12 * cdomg12 - clam12 * sdomg12; + comg12 = clam12 * cdomg12 + slam12 * sdomg12; + } + } + } + + if (outmask & GEOD_DISTANCE) + s12 = 0 + s12x; /* Convert -0 to 0 */ + + if (outmask & GEOD_REDUCEDLENGTH) + m12 = 0 + m12x; /* Convert -0 to 0 */ + + if (outmask & GEOD_AREA) { + real + /* From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) */ + salp0 = salp1 * cbet1, + calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */ + real alp12; + if (calp0 != 0 && salp0 != 0) { + real + /* From Lambda12: tan(bet) = tan(sig) * cos(alp) */ + ssig1 = sbet1, csig1 = calp1 * cbet1, + ssig2 = sbet2, csig2 = calp2 * cbet2, + k2 = sq(calp0) * g->ep2, + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2), + /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). */ + A4 = sq(g->a) * calp0 * salp0 * g->e2; + real B41, B42; + norm2(&ssig1, &csig1); + norm2(&ssig2, &csig2); + C4f(g, eps, Ca); + B41 = SinCosSeries(FALSE, ssig1, csig1, Ca, nC4); + B42 = SinCosSeries(FALSE, ssig2, csig2, Ca, nC4); + S12 = A4 * (B42 - B41); + } else + /* Avoid problems with indeterminate sig1, sig2 on equator */ + S12 = 0; + + if (!meridian && somg12 > 1) { + somg12 = sin(omg12); comg12 = cos(omg12); + } + + if (!meridian && + /* omg12 < 3/4 * pi */ + comg12 > -(real)(0.7071) && /* Long difference not too big */ + sbet2 - sbet1 < (real)(1.75)) { /* Lat difference not too big */ + /* Use tan(Gamma/2) = tan(omg12/2) + * * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2)) + * with tan(x/2) = sin(x)/(1+cos(x)) */ + real + domg12 = 1 + comg12, dbet1 = 1 + cbet1, dbet2 = 1 + cbet2; + alp12 = 2 * atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ), + domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) ); + } else { + /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ + real + salp12 = salp2 * calp1 - calp2 * salp1, + calp12 = calp2 * calp1 + salp2 * salp1; + /* The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz + * salp12 = -0 and alp12 = -180. However this depends on the sign + * being attached to 0 correctly. The following ensures the correct + * behavior. */ + if (salp12 == 0 && calp12 < 0) { + salp12 = tiny * calp1; + calp12 = -1; + } + alp12 = atan2(salp12, calp12); + } + S12 += g->c2 * alp12; + S12 *= swapp * lonsign * latsign; + /* Convert -0 to 0 */ + S12 += 0; + } + + /* Convert calp, salp to azimuth accounting for lonsign, swapp, latsign. */ + if (swapp < 0) { + swapx(&salp1, &salp2); + swapx(&calp1, &calp2); + if (outmask & GEOD_GEODESICSCALE) + swapx(&M12, &M21); + } + + salp1 *= swapp * lonsign; calp1 *= swapp * latsign; + salp2 *= swapp * lonsign; calp2 *= swapp * latsign; + + if (psalp1) *psalp1 = salp1; + if (pcalp1) *pcalp1 = calp1; + if (psalp2) *psalp2 = salp2; + if (pcalp2) *pcalp2 = calp2; + + if (outmask & GEOD_DISTANCE) + *ps12 = s12; + if (outmask & GEOD_REDUCEDLENGTH) + *pm12 = m12; + if (outmask & GEOD_GEODESICSCALE) { + if (pM12) *pM12 = M12; + if (pM21) *pM21 = M21; + } + if (outmask & GEOD_AREA) + *pS12 = S12; + + /* Returned value in [0, 180] */ + return a12; +} + +real geod_geninverse(const struct geod_geodesic* g, + real lat1, real lon1, real lat2, real lon2, + real* ps12, real* pazi1, real* pazi2, + real* pm12, real* pM12, real* pM21, real* pS12) { + real salp1, calp1, salp2, calp2, + a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, ps12, + &salp1, &calp1, &salp2, &calp2, + pm12, pM12, pM21, pS12); + if (pazi1) *pazi1 = atan2dx(salp1, calp1); + if (pazi2) *pazi2 = atan2dx(salp2, calp2); + return a12; +} + +void geod_inverseline(struct geod_geodesicline* l, + const struct geod_geodesic* g, + real lat1, real lon1, real lat2, real lon2, + unsigned caps) { + real salp1, calp1, + a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, 0, + &salp1, &calp1, 0, 0, + 0, 0, 0, 0), + azi1 = atan2dx(salp1, calp1); + caps = caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE; + /* Ensure that a12 can be converted to a distance */ + if (caps & (OUT_ALL & GEOD_DISTANCE_IN)) caps |= GEOD_DISTANCE; + geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); + geod_setarc(l, a12); +} + +void geod_inverse(const struct geod_geodesic* g, + real lat1, real lon1, real lat2, real lon2, + real* ps12, real* pazi1, real* pazi2) { + geod_geninverse(g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, 0, 0, 0, 0); +} + +real SinCosSeries(boolx sinp, real sinx, real cosx, const real c[], int n) { + /* Evaluate + * y = sinp ? sum(c[i] * sin( 2*i * x), i, 1, n) : + * sum(c[i] * cos((2*i+1) * x), i, 0, n-1) + * using Clenshaw summation. N.B. c[0] is unused for sin series + * Approx operation count = (n + 5) mult and (2 * n + 2) add */ + real ar, y0, y1; + c += (n + sinp); /* Point to one beyond last element */ + ar = 2 * (cosx - sinx) * (cosx + sinx); /* 2 * cos(2 * x) */ + y0 = (n & 1) ? *--c : 0; y1 = 0; /* accumulators for sum */ + /* Now n is even */ + n /= 2; + while (n--) { + /* Unroll loop x 2, so accumulators return to their original role */ + y1 = ar * y0 - y1 + *--c; + y0 = ar * y1 - y0 + *--c; + } + return sinp + ? 2 * sinx * cosx * y0 /* sin(2 * x) * y0 */ + : cosx * (y0 - y1); /* cos(x) * (y0 - y1) */ +} + +void Lengths(const struct geod_geodesic* g, + real eps, real sig12, + real ssig1, real csig1, real dn1, + real ssig2, real csig2, real dn2, + real cbet1, real cbet2, + real* ps12b, real* pm12b, real* pm0, + real* pM12, real* pM21, + /* Scratch area of the right size */ + real Ca[]) { + real m0 = 0, J12 = 0, A1 = 0, A2 = 0; + real Cb[nC]; + + /* Return m12b = (reduced length)/b; also calculate s12b = distance/b, + * and m0 = coefficient of secular term in expression for reduced length. */ + boolx redlp = pm12b || pm0 || pM12 || pM21; + if (ps12b || redlp) { + A1 = A1m1f(eps); + C1f(eps, Ca); + if (redlp) { + A2 = A2m1f(eps); + C2f(eps, Cb); + m0 = A1 - A2; + A2 = 1 + A2; + } + A1 = 1 + A1; + } + if (ps12b) { + real B1 = SinCosSeries(TRUE, ssig2, csig2, Ca, nC1) - + SinCosSeries(TRUE, ssig1, csig1, Ca, nC1); + /* Missing a factor of b */ + *ps12b = A1 * (sig12 + B1); + if (redlp) { + real B2 = SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - + SinCosSeries(TRUE, ssig1, csig1, Cb, nC2); + J12 = m0 * sig12 + (A1 * B1 - A2 * B2); + } + } else if (redlp) { + /* Assume here that nC1 >= nC2 */ + int l; + for (l = 1; l <= nC2; ++l) + Cb[l] = A1 * Ca[l] - A2 * Cb[l]; + J12 = m0 * sig12 + (SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - + SinCosSeries(TRUE, ssig1, csig1, Cb, nC2)); + } + if (pm0) *pm0 = m0; + if (pm12b) + /* Missing a factor of b. + * Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure + * accurate cancellation in the case of coincident points. */ + *pm12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - + csig1 * csig2 * J12; + if (pM12 || pM21) { + real csig12 = csig1 * csig2 + ssig1 * ssig2; + real t = g->ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2); + if (pM12) + *pM12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1; + if (pM21) + *pM21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2; + } +} + +real Astroid(real x, real y) { + /* Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive root k. + * This solution is adapted from Geocentric::Reverse. */ + real k; + real + p = sq(x), + q = sq(y), + r = (p + q - 1) / 6; + if ( !(q == 0 && r <= 0) ) { + real + /* Avoid possible division by zero when r = 0 by multiplying equations + * for s and t by r^3 and r, resp. */ + S = p * q / 4, /* S = r^3 * s */ + r2 = sq(r), + r3 = r * r2, + /* The discriminant of the quadratic equation for T3. This is zero on + * the evolute curve p^(1/3)+q^(1/3) = 1 */ + disc = S * (S + 2 * r3); + real u = r; + real v, uv, w; + if (disc >= 0) { + real T3 = S + r3, T; + /* Pick the sign on the sqrt to maximize abs(T3). This minimizes loss + * of precision due to cancellation. The result is unchanged because + * of the way the T is used in definition of u. */ + T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); /* T3 = (r * t)^3 */ + /* N.B. cbrtx always returns the real root. cbrtx(-8) = -2. */ + T = cbrtx(T3); /* T = r * t */ + /* T can be zero; but then r2 / T -> 0. */ + u += T + (T != 0 ? r2 / T : 0); + } else { + /* T is complex, but the way u is defined the result is real. */ + real ang = atan2(sqrt(-disc), -(S + r3)); + /* There are three possible cube roots. We choose the root which + * avoids cancellation. Note that disc < 0 implies that r < 0. */ + u += 2 * r * cos(ang / 3); + } + v = sqrt(sq(u) + q); /* guaranteed positive */ + /* Avoid loss of accuracy when u < 0. */ + uv = u < 0 ? q / (v - u) : u + v; /* u+v, guaranteed positive */ + w = (uv - q) / (2 * v); /* positive? */ + /* Rearrange expression for k to avoid loss of accuracy due to + * subtraction. Division by 0 not possible because uv > 0, w >= 0. */ + k = uv / (sqrt(uv + sq(w)) + w); /* guaranteed positive */ + } else { /* q == 0 && r <= 0 */ + /* y = 0 with |x| <= 1. Handle this case directly. + * for y small, positive root is k = abs(y)/sqrt(1-x^2) */ + k = 0; + } + return k; +} + +real InverseStart(const struct geod_geodesic* g, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real lam12, real slam12, real clam12, + real* psalp1, real* pcalp1, + /* Only updated if return val >= 0 */ + real* psalp2, real* pcalp2, + /* Only updated for short lines */ + real* pdnm, + /* Scratch area of the right size */ + real Ca[]) { + real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0, dnm = 0; + + /* Return a starting point for Newton's method in salp1 and calp1 (function + * value is -1). If Newton's method doesn't need to be used, return also + * salp2 and calp2 and function value is sig12. */ + real + sig12 = -1, /* Return value */ + /* bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0] */ + sbet12 = sbet2 * cbet1 - cbet2 * sbet1, + cbet12 = cbet2 * cbet1 + sbet2 * sbet1; + real sbet12a; + boolx shortline = cbet12 >= 0 && sbet12 < (real)(0.5) && + cbet2 * lam12 < (real)(0.5); + real somg12, comg12, ssig12, csig12; +#if defined(__GNUC__) && __GNUC__ == 4 && \ + (__GNUC_MINOR__ < 6 || defined(__MINGW32__)) + /* Volatile declaration needed to fix inverse cases + * 88.202499451857 0 -88.202499451857 179.981022032992859592 + * 89.262080389218 0 -89.262080389218 179.992207982775375662 + * 89.333123580033 0 -89.333123580032997687 179.99295812360148422 + * which otherwise fail with g++ 4.4.4 x86 -O3 (Linux) + * and g++ 4.4.0 (mingw) and g++ 4.6.1 (tdm mingw). */ + { + volatile real xx1 = sbet2 * cbet1; + volatile real xx2 = cbet2 * sbet1; + sbet12a = xx1 + xx2; + } +#else + sbet12a = sbet2 * cbet1 + cbet2 * sbet1; +#endif + if (shortline) { + real sbetm2 = sq(sbet1 + sbet2), omg12; + /* sin((bet1+bet2)/2)^2 + * = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) */ + sbetm2 /= sbetm2 + sq(cbet1 + cbet2); + dnm = sqrt(1 + g->ep2 * sbetm2); + omg12 = lam12 / (g->f1 * dnm); + somg12 = sin(omg12); comg12 = cos(omg12); + } else { + somg12 = slam12; comg12 = clam12; + } + + salp1 = cbet2 * somg12; + calp1 = comg12 >= 0 ? + sbet12 + cbet2 * sbet1 * sq(somg12) / (1 + comg12) : + sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); + + ssig12 = hypotx(salp1, calp1); + csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; + + if (shortline && ssig12 < g->etol2) { + /* really short lines */ + salp2 = cbet1 * somg12; + calp2 = sbet12 - cbet1 * sbet2 * + (comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12); + norm2(&salp2, &calp2); + /* Set return value */ + sig12 = atan2(ssig12, csig12); + } else if (fabs(g->n) > (real)(0.1) || /* No astroid calc if too eccentric */ + csig12 >= 0 || + ssig12 >= 6 * fabs(g->n) * pi * sq(cbet1)) { + /* Nothing to do, zeroth order spherical approximation is OK */ + } else { + /* Scale lam12 and bet2 to x, y coordinate system where antipodal point + * is at origin and singular point is at y = 0, x = -1. */ + real y, lamscale, betscale; + /* Volatile declaration needed to fix inverse case + * 56.320923501171 0 -56.320923501171 179.664747671772880215 + * which otherwise fails with g++ 4.4.4 x86 -O3 */ + volatile real x; + real lam12x = atan2(-slam12, -clam12); /* lam12 - pi */ + if (g->f >= 0) { /* In fact f == 0 does not get here */ + /* x = dlong, y = dlat */ + { + real + k2 = sq(sbet1) * g->ep2, + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); + lamscale = g->f * cbet1 * A3f(g, eps) * pi; + } + betscale = lamscale * cbet1; + + x = lam12x / lamscale; + y = sbet12a / betscale; + } else { /* f < 0 */ + /* x = dlat, y = dlong */ + real + cbet12a = cbet2 * cbet1 - sbet2 * sbet1, + bet12a = atan2(sbet12a, cbet12a); + real m12b, m0; + /* In the case of lon12 = 180, this repeats a calculation made in + * Inverse. */ + Lengths(g, g->n, pi + bet12a, + sbet1, -cbet1, dn1, sbet2, cbet2, dn2, + cbet1, cbet2, 0, &m12b, &m0, 0, 0, Ca); + x = -1 + m12b / (cbet1 * cbet2 * m0 * pi); + betscale = x < -(real)(0.01) ? sbet12a / x : + -g->f * sq(cbet1) * pi; + lamscale = betscale / cbet1; + y = lam12x / lamscale; + } + + if (y > -tol1 && x > -1 - xthresh) { + /* strip near cut */ + if (g->f >= 0) { + salp1 = minx((real)(1), -(real)(x)); calp1 = - sqrt(1 - sq(salp1)); + } else { + calp1 = maxx((real)(x > -tol1 ? 0 : -1), (real)(x)); + salp1 = sqrt(1 - sq(calp1)); + } + } else { + /* Estimate alp1, by solving the astroid problem. + * + * Could estimate alpha1 = theta + pi/2, directly, i.e., + * calp1 = y/k; salp1 = -x/(1+k); for f >= 0 + * calp1 = x/(1+k); salp1 = -y/k; for f < 0 (need to check) + * + * However, it's better to estimate omg12 from astroid and use + * spherical formula to compute alp1. This reduces the mean number of + * Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12 + * (min 0 max 5). The changes in the number of iterations are as + * follows: + * + * change percent + * 1 5 + * 0 78 + * -1 16 + * -2 0.6 + * -3 0.04 + * -4 0.002 + * + * The histogram of iterations is (m = number of iterations estimating + * alp1 directly, n = number of iterations estimating via omg12, total + * number of trials = 148605): + * + * iter m n + * 0 148 186 + * 1 13046 13845 + * 2 93315 102225 + * 3 36189 32341 + * 4 5396 7 + * 5 455 1 + * 6 56 0 + * + * Because omg12 is near pi, estimate work with omg12a = pi - omg12 */ + real k = Astroid(x, y); + real + omg12a = lamscale * ( g->f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k ); + somg12 = sin(omg12a); comg12 = -cos(omg12a); + /* Update spherical estimate of alp1 using omg12 instead of lam12 */ + salp1 = cbet2 * somg12; + calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); + } + } + /* Sanity check on starting guess. Backwards check allows NaN through. */ + if (!(salp1 <= 0)) + norm2(&salp1, &calp1); + else { + salp1 = 1; calp1 = 0; + } + + *psalp1 = salp1; + *pcalp1 = calp1; + if (shortline) + *pdnm = dnm; + if (sig12 >= 0) { + *psalp2 = salp2; + *pcalp2 = calp2; + } + return sig12; +} + +real Lambda12(const struct geod_geodesic* g, + real sbet1, real cbet1, real dn1, + real sbet2, real cbet2, real dn2, + real salp1, real calp1, + real slam120, real clam120, + real* psalp2, real* pcalp2, + real* psig12, + real* pssig1, real* pcsig1, + real* pssig2, real* pcsig2, + real* peps, + real* pdomg12, + boolx diffp, real* pdlam12, + /* Scratch area of the right size */ + real Ca[]) { + real salp2 = 0, calp2 = 0, sig12 = 0, + ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, + domg12 = 0, dlam12 = 0; + real salp0, calp0; + real somg1, comg1, somg2, comg2, somg12, comg12, lam12; + real B312, eta, k2; + + if (sbet1 == 0 && calp1 == 0) + /* Break degeneracy of equatorial line. This case has already been + * handled. */ + calp1 = -tiny; + + /* sin(alp1) * cos(bet1) = sin(alp0) */ + salp0 = salp1 * cbet1; + calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */ + + /* tan(bet1) = tan(sig1) * cos(alp1) + * tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1) */ + ssig1 = sbet1; somg1 = salp0 * sbet1; + csig1 = comg1 = calp1 * cbet1; + norm2(&ssig1, &csig1); + /* norm2(&somg1, &comg1); -- don't need to normalize! */ + + /* Enforce symmetries in the case abs(bet2) = -bet1. Need to be careful + * about this case, since this can yield singularities in the Newton + * iteration. + * sin(alp2) * cos(bet2) = sin(alp0) */ + salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1; + /* calp2 = sqrt(1 - sq(salp2)) + * = sqrt(sq(calp0) - sq(sbet2)) / cbet2 + * and subst for calp0 and rearrange to give (choose positive sqrt + * to give alp2 in [0, pi/2]). */ + calp2 = cbet2 != cbet1 || fabs(sbet2) != -sbet1 ? + sqrt(sq(calp1 * cbet1) + + (cbet1 < -sbet1 ? + (cbet2 - cbet1) * (cbet1 + cbet2) : + (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 : + fabs(calp1); + /* tan(bet2) = tan(sig2) * cos(alp2) + * tan(omg2) = sin(alp0) * tan(sig2). */ + ssig2 = sbet2; somg2 = salp0 * sbet2; + csig2 = comg2 = calp2 * cbet2; + norm2(&ssig2, &csig2); + /* norm2(&somg2, &comg2); -- don't need to normalize! */ + + /* sig12 = sig2 - sig1, limit to [0, pi] */ + sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2), + csig1 * csig2 + ssig1 * ssig2); + + /* omg12 = omg2 - omg1, limit to [0, pi] */ + somg12 = maxx((real)(0), comg1 * somg2 - somg1 * comg2); + comg12 = comg1 * comg2 + somg1 * somg2; + /* eta = omg12 - lam120 */ + eta = atan2(somg12 * clam120 - comg12 * slam120, + comg12 * clam120 + somg12 * slam120); + k2 = sq(calp0) * g->ep2; + eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); + C3f(g, eps, Ca); + B312 = (SinCosSeries(TRUE, ssig2, csig2, Ca, nC3-1) - + SinCosSeries(TRUE, ssig1, csig1, Ca, nC3-1)); + domg12 = -g->f * A3f(g, eps) * salp0 * (sig12 + B312); + lam12 = eta + domg12; + + if (diffp) { + if (calp2 == 0) + dlam12 = - 2 * g->f1 * dn1 / sbet1; + else { + Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, + cbet1, cbet2, 0, &dlam12, 0, 0, 0, Ca); + dlam12 *= g->f1 / (calp2 * cbet2); + } + } + + *psalp2 = salp2; + *pcalp2 = calp2; + *psig12 = sig12; + *pssig1 = ssig1; + *pcsig1 = csig1; + *pssig2 = ssig2; + *pcsig2 = csig2; + *peps = eps; + *pdomg12 = domg12; + if (diffp) + *pdlam12 = dlam12; + + return lam12; +} + +real A3f(const struct geod_geodesic* g, real eps) { + /* Evaluate A3 */ + return polyval(nA3 - 1, g->A3x, eps); +} + +void C3f(const struct geod_geodesic* g, real eps, real c[]) { + /* Evaluate C3 coeffs + * Elements c[1] through c[nC3 - 1] are set */ + real mult = 1; + int o = 0, l; + for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ + int m = nC3 - l - 1; /* order of polynomial in eps */ + mult *= eps; + c[l] = mult * polyval(m, g->C3x + o, eps); + o += m + 1; + } +} + +void C4f(const struct geod_geodesic* g, real eps, real c[]) { + /* Evaluate C4 coeffs + * Elements c[0] through c[nC4 - 1] are set */ + real mult = 1; + int o = 0, l; + for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ + int m = nC4 - l - 1; /* order of polynomial in eps */ + c[l] = mult * polyval(m, g->C4x + o, eps); + o += m + 1; + mult *= eps; + } +} + +/* The scale factor A1-1 = mean value of (d/dsigma)I1 - 1 */ +real A1m1f(real eps) { + static const real coeff[] = { + /* (1-eps)*A1-1, polynomial in eps2 of order 3 */ + 1, 4, 64, 0, 256, + }; + int m = nA1/2; + real t = polyval(m, coeff, sq(eps)) / coeff[m + 1]; + return (t + eps) / (1 - eps); +} + +/* The coefficients C1[l] in the Fourier expansion of B1 */ +void C1f(real eps, real c[]) { + static const real coeff[] = { + /* C1[1]/eps^1, polynomial in eps2 of order 2 */ + -1, 6, -16, 32, + /* C1[2]/eps^2, polynomial in eps2 of order 2 */ + -9, 64, -128, 2048, + /* C1[3]/eps^3, polynomial in eps2 of order 1 */ + 9, -16, 768, + /* C1[4]/eps^4, polynomial in eps2 of order 1 */ + 3, -5, 512, + /* C1[5]/eps^5, polynomial in eps2 of order 0 */ + -7, 1280, + /* C1[6]/eps^6, polynomial in eps2 of order 0 */ + -7, 2048, + }; + real + eps2 = sq(eps), + d = eps; + int o = 0, l; + for (l = 1; l <= nC1; ++l) { /* l is index of C1p[l] */ + int m = (nC1 - l) / 2; /* order of polynomial in eps^2 */ + c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } +} + +/* The coefficients C1p[l] in the Fourier expansion of B1p */ +void C1pf(real eps, real c[]) { + static const real coeff[] = { + /* C1p[1]/eps^1, polynomial in eps2 of order 2 */ + 205, -432, 768, 1536, + /* C1p[2]/eps^2, polynomial in eps2 of order 2 */ + 4005, -4736, 3840, 12288, + /* C1p[3]/eps^3, polynomial in eps2 of order 1 */ + -225, 116, 384, + /* C1p[4]/eps^4, polynomial in eps2 of order 1 */ + -7173, 2695, 7680, + /* C1p[5]/eps^5, polynomial in eps2 of order 0 */ + 3467, 7680, + /* C1p[6]/eps^6, polynomial in eps2 of order 0 */ + 38081, 61440, + }; + real + eps2 = sq(eps), + d = eps; + int o = 0, l; + for (l = 1; l <= nC1p; ++l) { /* l is index of C1p[l] */ + int m = (nC1p - l) / 2; /* order of polynomial in eps^2 */ + c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } +} + +/* The scale factor A2-1 = mean value of (d/dsigma)I2 - 1 */ +real A2m1f(real eps) { + static const real coeff[] = { + /* (eps+1)*A2-1, polynomial in eps2 of order 3 */ + -11, -28, -192, 0, 256, + }; + int m = nA2/2; + real t = polyval(m, coeff, sq(eps)) / coeff[m + 1]; + return (t - eps) / (1 + eps); +} + +/* The coefficients C2[l] in the Fourier expansion of B2 */ +void C2f(real eps, real c[]) { + static const real coeff[] = { + /* C2[1]/eps^1, polynomial in eps2 of order 2 */ + 1, 2, 16, 32, + /* C2[2]/eps^2, polynomial in eps2 of order 2 */ + 35, 64, 384, 2048, + /* C2[3]/eps^3, polynomial in eps2 of order 1 */ + 15, 80, 768, + /* C2[4]/eps^4, polynomial in eps2 of order 1 */ + 7, 35, 512, + /* C2[5]/eps^5, polynomial in eps2 of order 0 */ + 63, 1280, + /* C2[6]/eps^6, polynomial in eps2 of order 0 */ + 77, 2048, + }; + real + eps2 = sq(eps), + d = eps; + int o = 0, l; + for (l = 1; l <= nC2; ++l) { /* l is index of C2[l] */ + int m = (nC2 - l) / 2; /* order of polynomial in eps^2 */ + c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1]; + o += m + 2; + d *= eps; + } +} + +/* The scale factor A3 = mean value of (d/dsigma)I3 */ +void A3coeff(struct geod_geodesic* g) { + static const real coeff[] = { + /* A3, coeff of eps^5, polynomial in n of order 0 */ + -3, 128, + /* A3, coeff of eps^4, polynomial in n of order 1 */ + -2, -3, 64, + /* A3, coeff of eps^3, polynomial in n of order 2 */ + -1, -3, -1, 16, + /* A3, coeff of eps^2, polynomial in n of order 2 */ + 3, -1, -2, 8, + /* A3, coeff of eps^1, polynomial in n of order 1 */ + 1, -1, 2, + /* A3, coeff of eps^0, polynomial in n of order 0 */ + 1, 1, + }; + int o = 0, k = 0, j; + for (j = nA3 - 1; j >= 0; --j) { /* coeff of eps^j */ + int m = nA3 - j - 1 < j ? nA3 - j - 1 : j; /* order of polynomial in n */ + g->A3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; + o += m + 2; + } +} + +/* The coefficients C3[l] in the Fourier expansion of B3 */ +void C3coeff(struct geod_geodesic* g) { + static const real coeff[] = { + /* C3[1], coeff of eps^5, polynomial in n of order 0 */ + 3, 128, + /* C3[1], coeff of eps^4, polynomial in n of order 1 */ + 2, 5, 128, + /* C3[1], coeff of eps^3, polynomial in n of order 2 */ + -1, 3, 3, 64, + /* C3[1], coeff of eps^2, polynomial in n of order 2 */ + -1, 0, 1, 8, + /* C3[1], coeff of eps^1, polynomial in n of order 1 */ + -1, 1, 4, + /* C3[2], coeff of eps^5, polynomial in n of order 0 */ + 5, 256, + /* C3[2], coeff of eps^4, polynomial in n of order 1 */ + 1, 3, 128, + /* C3[2], coeff of eps^3, polynomial in n of order 2 */ + -3, -2, 3, 64, + /* C3[2], coeff of eps^2, polynomial in n of order 2 */ + 1, -3, 2, 32, + /* C3[3], coeff of eps^5, polynomial in n of order 0 */ + 7, 512, + /* C3[3], coeff of eps^4, polynomial in n of order 1 */ + -10, 9, 384, + /* C3[3], coeff of eps^3, polynomial in n of order 2 */ + 5, -9, 5, 192, + /* C3[4], coeff of eps^5, polynomial in n of order 0 */ + 7, 512, + /* C3[4], coeff of eps^4, polynomial in n of order 1 */ + -14, 7, 512, + /* C3[5], coeff of eps^5, polynomial in n of order 0 */ + 21, 2560, + }; + int o = 0, k = 0, l, j; + for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ + for (j = nC3 - 1; j >= l; --j) { /* coeff of eps^j */ + int m = nC3 - j - 1 < j ? nC3 - j - 1 : j; /* order of polynomial in n */ + g->C3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; + o += m + 2; + } + } +} + +/* The coefficients C4[l] in the Fourier expansion of I4 */ +void C4coeff(struct geod_geodesic* g) { + static const real coeff[] = { + /* C4[0], coeff of eps^5, polynomial in n of order 0 */ + 97, 15015, + /* C4[0], coeff of eps^4, polynomial in n of order 1 */ + 1088, 156, 45045, + /* C4[0], coeff of eps^3, polynomial in n of order 2 */ + -224, -4784, 1573, 45045, + /* C4[0], coeff of eps^2, polynomial in n of order 3 */ + -10656, 14144, -4576, -858, 45045, + /* C4[0], coeff of eps^1, polynomial in n of order 4 */ + 64, 624, -4576, 6864, -3003, 15015, + /* C4[0], coeff of eps^0, polynomial in n of order 5 */ + 100, 208, 572, 3432, -12012, 30030, 45045, + /* C4[1], coeff of eps^5, polynomial in n of order 0 */ + 1, 9009, + /* C4[1], coeff of eps^4, polynomial in n of order 1 */ + -2944, 468, 135135, + /* C4[1], coeff of eps^3, polynomial in n of order 2 */ + 5792, 1040, -1287, 135135, + /* C4[1], coeff of eps^2, polynomial in n of order 3 */ + 5952, -11648, 9152, -2574, 135135, + /* C4[1], coeff of eps^1, polynomial in n of order 4 */ + -64, -624, 4576, -6864, 3003, 135135, + /* C4[2], coeff of eps^5, polynomial in n of order 0 */ + 8, 10725, + /* C4[2], coeff of eps^4, polynomial in n of order 1 */ + 1856, -936, 225225, + /* C4[2], coeff of eps^3, polynomial in n of order 2 */ + -8448, 4992, -1144, 225225, + /* C4[2], coeff of eps^2, polynomial in n of order 3 */ + -1440, 4160, -4576, 1716, 225225, + /* C4[3], coeff of eps^5, polynomial in n of order 0 */ + -136, 63063, + /* C4[3], coeff of eps^4, polynomial in n of order 1 */ + 1024, -208, 105105, + /* C4[3], coeff of eps^3, polynomial in n of order 2 */ + 3584, -3328, 1144, 315315, + /* C4[4], coeff of eps^5, polynomial in n of order 0 */ + -128, 135135, + /* C4[4], coeff of eps^4, polynomial in n of order 1 */ + -2560, 832, 405405, + /* C4[5], coeff of eps^5, polynomial in n of order 0 */ + 128, 99099, + }; + int o = 0, k = 0, l, j; + for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ + for (j = nC4 - 1; j >= l; --j) { /* coeff of eps^j */ + int m = nC4 - j - 1; /* order of polynomial in n */ + g->C4x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1]; + o += m + 2; + } + } +} + +int transit(real lon1, real lon2) { + real lon12; + /* Return 1 or -1 if crossing prime meridian in east or west direction. + * Otherwise return zero. */ + /* Compute lon12 the same way as Geodesic::Inverse. */ + lon1 = AngNormalize(lon1); + lon2 = AngNormalize(lon2); + lon12 = AngDiff(lon1, lon2, 0); + return lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 : + (lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0); +} + +int transitdirect(real lon1, real lon2) { +#if HAVE_C99_MATH + lon1 = remainder(lon1, (real)(720)); + lon2 = remainder(lon2, (real)(720)); + return ( (lon2 <= 0 && lon2 > -360 ? 1 : 0) - + (lon1 <= 0 && lon1 > -360 ? 1 : 0) ); +#else + lon1 = fmod(lon1, (real)(720)); + lon2 = fmod(lon2, (real)(720)); + return ( ((lon2 <= 0 && lon2 > -360) || lon2 > 360 ? 1 : 0) - + ((lon1 <= 0 && lon1 > -360) || lon1 > 360 ? 1 : 0) ); +#endif +} + +void accini(real s[]) { + /* Initialize an accumulator; this is an array with two elements. */ + s[0] = s[1] = 0; +} + +void acccopy(const real s[], real t[]) { + /* Copy an accumulator; t = s. */ + t[0] = s[0]; t[1] = s[1]; +} + +void accadd(real s[], real y) { + /* Add y to an accumulator. */ + real u, z = sumx(y, s[1], &u); + s[0] = sumx(z, s[0], &s[1]); + if (s[0] == 0) + s[0] = u; + else + s[1] = s[1] + u; +} + +real accsum(const real s[], real y) { + /* Return accumulator + y (but don't add to accumulator). */ + real t[2]; + acccopy(s, t); + accadd(t, y); + return t[0]; +} + +void accneg(real s[]) { + /* Negate an accumulator. */ + s[0] = -s[0]; s[1] = -s[1]; +} + +void geod_polygon_init(struct geod_polygon* p, boolx polylinep) { + p->polyline = (polylinep != 0); + geod_polygon_clear(p); +} + +void geod_polygon_clear(struct geod_polygon* p) { + p->lat0 = p->lon0 = p->lat = p->lon = NaN; + accini(p->P); + accini(p->A); + p->num = p->crossings = 0; +} + +void geod_polygon_addpoint(const struct geod_geodesic* g, + struct geod_polygon* p, + real lat, real lon) { + lon = AngNormalize(lon); + if (p->num == 0) { + p->lat0 = p->lat = lat; + p->lon0 = p->lon = lon; + } else { + real s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ + geod_geninverse(g, p->lat, p->lon, lat, lon, + &s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12); + accadd(p->P, s12); + if (!p->polyline) { + accadd(p->A, S12); + p->crossings += transit(p->lon, lon); + } + p->lat = lat; p->lon = lon; + } + ++p->num; +} + +void geod_polygon_addedge(const struct geod_geodesic* g, + struct geod_polygon* p, + real azi, real s) { + if (p->num) { /* Do nothing is num is zero */ + /* Initialize S12 to stop Visual Studio warning. Initialization of lat and + * lon is to make CLang static analyzer happy. */ + real lat = 0, lon = 0, S12 = 0; + geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, + &lat, &lon, 0, + 0, 0, 0, 0, p->polyline ? 0 : &S12); + accadd(p->P, s); + if (!p->polyline) { + accadd(p->A, S12); + p->crossings += transitdirect(p->lon, lon); + } + p->lat = lat; p->lon = lon; + ++p->num; + } +} + +unsigned geod_polygon_compute(const struct geod_geodesic* g, + const struct geod_polygon* p, + boolx reverse, boolx sign, + real* pA, real* pP) { + real s12, S12, t[2], area0; + int crossings; + if (p->num < 2) { + if (pP) *pP = 0; + if (!p->polyline && pA) *pA = 0; + return p->num; + } + if (p->polyline) { + if (pP) *pP = p->P[0]; + return p->num; + } + geod_geninverse(g, p->lat, p->lon, p->lat0, p->lon0, + &s12, 0, 0, 0, 0, 0, &S12); + if (pP) *pP = accsum(p->P, s12); + acccopy(p->A, t); + accadd(t, S12); + crossings = p->crossings + transit(p->lon, p->lon0); + area0 = 4 * pi * g->c2; + if (crossings & 1) + accadd(t, (t[0] < 0 ? 1 : -1) * area0/2); + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + accneg(t); + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + if (sign) { + if (t[0] > area0/2) + accadd(t, -area0); + else if (t[0] <= -area0/2) + accadd(t, +area0); + } else { + if (t[0] >= area0) + accadd(t, -area0); + else if (t[0] < 0) + accadd(t, +area0); + } + if (pA) *pA = 0 + t[0]; + return p->num; +} + +unsigned geod_polygon_testpoint(const struct geod_geodesic* g, + const struct geod_polygon* p, + real lat, real lon, + boolx reverse, boolx sign, + real* pA, real* pP) { + real perimeter, tempsum, area0; + int crossings, i; + unsigned num = p->num + 1; + if (num == 1) { + if (pP) *pP = 0; + if (!p->polyline && pA) *pA = 0; + return num; + } + perimeter = p->P[0]; + tempsum = p->polyline ? 0 : p->A[0]; + crossings = p->crossings; + for (i = 0; i < (p->polyline ? 1 : 2); ++i) { + real s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ + geod_geninverse(g, + i == 0 ? p->lat : lat, i == 0 ? p->lon : lon, + i != 0 ? p->lat0 : lat, i != 0 ? p->lon0 : lon, + &s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12); + perimeter += s12; + if (!p->polyline) { + tempsum += S12; + crossings += transit(i == 0 ? p->lon : lon, + i != 0 ? p->lon0 : lon); + } + } + + if (pP) *pP = perimeter; + if (p->polyline) + return num; + + area0 = 4 * pi * g->c2; + if (crossings & 1) + tempsum += (tempsum < 0 ? 1 : -1) * area0/2; + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + tempsum *= -1; + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + if (sign) { + if (tempsum > area0/2) + tempsum -= area0; + else if (tempsum <= -area0/2) + tempsum += area0; + } else { + if (tempsum >= area0) + tempsum -= area0; + else if (tempsum < 0) + tempsum += area0; + } + if (pA) *pA = 0 + tempsum; + return num; +} + +unsigned geod_polygon_testedge(const struct geod_geodesic* g, + const struct geod_polygon* p, + real azi, real s, + boolx reverse, boolx sign, + real* pA, real* pP) { + real perimeter, tempsum, area0; + int crossings; + unsigned num = p->num + 1; + if (num == 1) { /* we don't have a starting point! */ + if (pP) *pP = NaN; + if (!p->polyline && pA) *pA = NaN; + return 0; + } + perimeter = p->P[0] + s; + if (p->polyline) { + if (pP) *pP = perimeter; + return num; + } + + tempsum = p->A[0]; + crossings = p->crossings; + { + /* Initialization of lat, lon, and S12 is to make CLang static analyzer + happy. */ + real lat = 0, lon = 0, s12, S12 = 0; + geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, + &lat, &lon, 0, + 0, 0, 0, 0, &S12); + tempsum += S12; + crossings += transitdirect(p->lon, lon); + geod_geninverse(g, lat, lon, p->lat0, p->lon0, + &s12, 0, 0, 0, 0, 0, &S12); + perimeter += s12; + tempsum += S12; + crossings += transit(lon, p->lon0); + } + + area0 = 4 * pi * g->c2; + if (crossings & 1) + tempsum += (tempsum < 0 ? 1 : -1) * area0/2; + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + tempsum *= -1; + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + if (sign) { + if (tempsum > area0/2) + tempsum -= area0; + else if (tempsum <= -area0/2) + tempsum += area0; + } else { + if (tempsum >= area0) + tempsum -= area0; + else if (tempsum < 0) + tempsum += area0; + } + if (pP) *pP = perimeter; + if (pA) *pA = 0 + tempsum; + return num; +} + +void geod_polygonarea(const struct geod_geodesic* g, + real lats[], real lons[], int n, + real* pA, real* pP) { + int i; + struct geod_polygon p; + geod_polygon_init(&p, FALSE); + for (i = 0; i < n; ++i) + geod_polygon_addpoint(g, &p, lats[i], lons[i]); + geod_polygon_compute(g, &p, FALSE, TRUE, pA, pP); +} + +/** @endcond */ diff --git a/src/geodtest.c b/src/geodtest.c deleted file mode 100644 index 0ee86d5c..00000000 --- a/src/geodtest.c +++ /dev/null @@ -1,1069 +0,0 @@ -/** - * \file geodtest.c - * \brief Test suite for the geodesic routines in C - * - * Run these tests by configuring with cmake and running "make test". - * - * Copyright (c) Charles Karney (2015-2018) and licensed - * under the MIT/X11 License. For more information, see - * https://geographiclib.sourceforge.io/ - **********************************************************************/ - -/** @cond SKIP */ - -#include "geodesic.h" -#include -#include - -#if defined(_MSC_VER) -/* Squelch warnings about assignment within conditional expression */ -# pragma warning (disable: 4706) -#endif - -static const double wgs84_a = 6378137, wgs84_f = 1/298.257223563; /* WGS84 */ - -static int checkEquals(double x, double y, double d) { - if (fabs(x - y) <= d) - return 0; - printf("checkEquals fails: %.7g != %.7g +/- %.7g\n", x, y, d); - return 1; -} - -static int checkNaN(double x) { - if (x != x) - return 0; - printf("checkNaN fails: %.7g\n", x); - return 1; -} - -static const int ncases = 20; -static const double testcases[20][12] = { - {35.60777, -139.44815, 111.098748429560326, - -11.17491, -69.95921, 129.289270889708762, - 8935244.5604818305, 80.50729714281974, 6273170.2055303837, - 0.16606318447386067, 0.16479116945612937, 12841384694976.432}, - {55.52454, 106.05087, 22.020059880982801, - 77.03196, 197.18234, 109.112041110671519, - 4105086.1713924406, 36.892740690445894, 3828869.3344387607, - 0.80076349608092607, 0.80101006984201008, 61674961290615.615}, - {-21.97856, 142.59065, -32.44456876433189, - 41.84138, 98.56635, -41.84359951440466, - 8394328.894657671, 75.62930491011522, 6161154.5773110616, - 0.24816339233950381, 0.24930251203627892, -6637997720646.717}, - {-66.99028, 112.2363, 173.73491240878403, - -12.70631, 285.90344, 2.512956620913668, - 11150344.2312080241, 100.278634181155759, 6289939.5670446687, - -0.17199490274700385, -0.17722569526345708, -121287239862139.744}, - {-17.42761, 173.34268, -159.033557661192928, - -15.84784, 5.93557, -20.787484651536988, - 16076603.1631180673, 144.640108810286253, 3732902.1583877189, - -0.81273638700070476, -0.81299800519154474, 97825992354058.708}, - {32.84994, 48.28919, 150.492927788121982, - -56.28556, 202.29132, 48.113449399816759, - 16727068.9438164461, 150.565799985466607, 3147838.1910180939, - -0.87334918086923126, -0.86505036767110637, -72445258525585.010}, - {6.96833, 52.74123, 92.581585386317712, - -7.39675, 206.17291, 90.721692165923907, - 17102477.2496958388, 154.147366239113561, 2772035.6169917581, - -0.89991282520302447, -0.89986892177110739, -1311796973197.995}, - {-50.56724, -16.30485, -105.439679907590164, - -33.56571, -94.97412, -47.348547835650331, - 6455670.5118668696, 58.083719495371259, 5409150.7979815838, - 0.53053508035997263, 0.52988722644436602, 41071447902810.047}, - {-58.93002, -8.90775, 140.965397902500679, - -8.91104, 133.13503, 19.255429433416599, - 11756066.0219864627, 105.755691241406877, 6151101.2270708536, - -0.26548622269867183, -0.27068483874510741, -86143460552774.735}, - {-68.82867, -74.28391, 93.774347763114881, - -50.63005, -8.36685, 34.65564085411343, - 3956936.926063544, 35.572254987389284, 3708890.9544062657, - 0.81443963736383502, 0.81420859815358342, -41845309450093.787}, - {-10.62672, -32.0898, -86.426713286747751, - 5.883, -134.31681, -80.473780971034875, - 11470869.3864563009, 103.387395634504061, 6184411.6622659713, - -0.23138683500430237, -0.23155097622286792, 4198803992123.548}, - {-21.76221, 166.90563, 29.319421206936428, - 48.72884, 213.97627, 43.508671946410168, - 9098627.3986554915, 81.963476716121964, 6299240.9166992283, - 0.13965943368590333, 0.14152969707656796, 10024709850277.476}, - {-19.79938, -174.47484, 71.167275780171533, - -11.99349, -154.35109, 65.589099775199228, - 2319004.8601169389, 20.896611684802389, 2267960.8703918325, - 0.93427001867125849, 0.93424887135032789, -3935477535005.785}, - {-11.95887, -116.94513, 92.712619830452549, - 4.57352, 7.16501, 78.64960934409585, - 13834722.5801401374, 124.688684161089762, 5228093.177931598, - -0.56879356755666463, -0.56918731952397221, -9919582785894.853}, - {-87.85331, 85.66836, -65.120313040242748, - 66.48646, 16.09921, -4.888658719272296, - 17286615.3147144645, 155.58592449699137, 2635887.4729110181, - -0.90697975771398578, -0.91095608883042767, 42667211366919.534}, - {1.74708, 128.32011, -101.584843631173858, - -11.16617, 11.87109, -86.325793296437476, - 12942901.1241347408, 116.650512484301857, 5682744.8413270572, - -0.44857868222697644, -0.44824490340007729, 10763055294345.653}, - {-25.72959, -144.90758, -153.647468693117198, - -57.70581, -269.17879, -48.343983158876487, - 9413446.7452453107, 84.664533838404295, 6356176.6898881281, - 0.09492245755254703, 0.09737058264766572, 74515122850712.444}, - {-41.22777, 122.32875, 14.285113402275739, - -7.57291, 130.37946, 10.805303085187369, - 3812686.035106021, 34.34330804743883, 3588703.8812128856, - 0.82605222593217889, 0.82572158200920196, -2456961531057.857}, - {11.01307, 138.25278, 79.43682622782374, - 6.62726, 247.05981, 103.708090215522657, - 11911190.819018408, 107.341669954114577, 6070904.722786735, - -0.29767608923657404, -0.29785143390252321, 17121631423099.696}, - {-29.47124, 95.14681, -163.779130441688382, - -27.46601, -69.15955, -15.909335945554969, - 13487015.8381145492, 121.294026715742277, 5481428.9945736388, - -0.51527225545373252, -0.51556587964721788, 104679964020340.318}}; - -static int testinverse() { - double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; - double azi1a, azi2a, s12a, a12a, m12a, M12a, M21a, S12a; - struct geod_geodesic g; - int i, result = 0; - geod_init(&g, wgs84_a, wgs84_f); - for (i = 0; i < ncases; ++i) { - lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; - lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; - s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; - M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; - a12a = geod_geninverse(&g, lat1, lon1, lat2, lon2, &s12a, &azi1a, &azi2a, - &m12a, &M12a, &M21a, &S12a); - result += checkEquals(azi1, azi1a, 1e-13); - result += checkEquals(azi2, azi2a, 1e-13); - result += checkEquals(s12, s12a, 1e-8); - result += checkEquals(a12, a12a, 1e-13); - result += checkEquals(m12, m12a, 1e-8); - result += checkEquals(M12, M12a, 1e-15); - result += checkEquals(M21, M21a, 1e-15); - result += checkEquals(S12, S12a, 0.1); - } - return result; -} - -static int testdirect() { - double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; - double lat2a, lon2a, azi2a, a12a, m12a, M12a, M21a, S12a; - struct geod_geodesic g; - int i, result = 0; - unsigned flags = GEOD_LONG_UNROLL; - geod_init(&g, wgs84_a, wgs84_f); - for (i = 0; i < ncases; ++i) { - lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; - lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; - s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; - M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; - a12a = geod_gendirect(&g, lat1, lon1, azi1, flags, s12, - &lat2a, &lon2a, &azi2a, 0, - &m12a, &M12a, &M21a, &S12a); - result += checkEquals(lat2, lat2a, 1e-13); - result += checkEquals(lon2, lon2a, 1e-13); - result += checkEquals(azi2, azi2a, 1e-13); - result += checkEquals(a12, a12a, 1e-13); - result += checkEquals(m12, m12a, 1e-8); - result += checkEquals(M12, M12a, 1e-15); - result += checkEquals(M21, M21a, 1e-15); - result += checkEquals(S12, S12a, 0.1); - } - return result; -} - -static int testarcdirect() { - double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; - double lat2a, lon2a, azi2a, s12a, m12a, M12a, M21a, S12a; - struct geod_geodesic g; - int i, result = 0; - unsigned flags = GEOD_ARCMODE | GEOD_LONG_UNROLL; - geod_init(&g, wgs84_a, wgs84_f); - for (i = 0; i < ncases; ++i) { - lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; - lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; - s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; - M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; - geod_gendirect(&g, lat1, lon1, azi1, flags, a12, - &lat2a, &lon2a, &azi2a, &s12a, &m12a, &M12a, &M21a, &S12a); - result += checkEquals(lat2, lat2a, 1e-13); - result += checkEquals(lon2, lon2a, 1e-13); - result += checkEquals(azi2, azi2a, 1e-13); - result += checkEquals(s12, s12a, 1e-8); - result += checkEquals(m12, m12a, 1e-8); - result += checkEquals(M12, M12a, 1e-15); - result += checkEquals(M21, M21a, 1e-15); - result += checkEquals(S12, S12a, 0.1); - } - return result; -} - -static int GeodSolve0() { - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 40.6, -73.8, 49.01666667, 2.55, &s12, &azi1, &azi2); - result += checkEquals(azi1, 53.47022, 0.5e-5); - result += checkEquals(azi2, 111.59367, 0.5e-5); - result += checkEquals(s12, 5853226, 0.5); - return result; -} - -static int GeodSolve1() { - double lat2, lon2, azi2; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_direct(&g, 40.63972222, -73.77888889, 53.5, 5850e3, - &lat2, &lon2, &azi2); - result += checkEquals(lat2, 49.01467, 0.5e-5); - result += checkEquals(lon2, 2.56106, 0.5e-5); - result += checkEquals(azi2, 111.62947, 0.5e-5); - return result; -} - -static int GeodSolve2() { - /* Check fix for antipodal prolate bug found 2010-09-04 */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, 6.4e6, -1/150.0); - geod_inverse(&g, 0.07476, 0, -0.07476, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00078, 0.5e-5); - result += checkEquals(azi2, 90.00078, 0.5e-5); - result += checkEquals(s12, 20106193, 0.5); - geod_inverse(&g, 0.1, 0, -0.1, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00105, 0.5e-5); - result += checkEquals(azi2, 90.00105, 0.5e-5); - result += checkEquals(s12, 20106193, 0.5); - return result; -} - -static int GeodSolve4() { - /* Check fix for short line bug found 2010-05-21 */ - double s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 36.493349428792, 0, 36.49334942879201, .0000008, - &s12, 0, 0); - result += checkEquals(s12, 0.072, 0.5e-3); - return result; -} - -static int GeodSolve5() { - /* Check fix for point2=pole bug found 2010-05-03 */ - double lat2, lon2, azi2; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_direct(&g, 0.01777745589997, 30, 0, 10e6, &lat2, &lon2, &azi2); - result += checkEquals(lat2, 90, 0.5e-5); - if (lon2 < 0) { - result += checkEquals(lon2, -150, 0.5e-5); - result += checkEquals(fabs(azi2), 180, 0.5e-5); - } else { - result += checkEquals(lon2, 30, 0.5e-5); - result += checkEquals(azi2, 0, 0.5e-5); - } - return result; -} - -static int GeodSolve6() { - /* Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4 - * x86 -O3). Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1). */ - double s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 88.202499451857, 0, - -88.202499451857, 179.981022032992859592, &s12, 0, 0); - result += checkEquals(s12, 20003898.214, 0.5e-3); - geod_inverse(&g, 89.262080389218, 0, - -89.262080389218, 179.992207982775375662, &s12, 0, 0); - result += checkEquals(s12, 20003925.854, 0.5e-3); - geod_inverse(&g, 89.333123580033, 0, - -89.333123580032997687, 179.99295812360148422, &s12, 0, 0); - result += checkEquals(s12, 20003926.881, 0.5e-3); - return result; -} - -static int GeodSolve9() { - /* Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3) */ - double s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 56.320923501171, 0, - -56.320923501171, 179.664747671772880215, &s12, 0, 0); - result += checkEquals(s12, 19993558.287, 0.5e-3); - return result; -} - -static int GeodSolve10() { - /* Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio - * 10 rel + debug) */ - double s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 52.784459512564, 0, - -52.784459512563990912, 179.634407464943777557, &s12, 0, 0); - result += checkEquals(s12, 19991596.095, 0.5e-3); - return result; -} - -static int GeodSolve11() { - /* Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio - * 10 rel + debug) */ - double s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 48.522876735459, 0, - -48.52287673545898293, 179.599720456223079643, &s12, 0, 0); - result += checkEquals(s12, 19989144.774, 0.5e-3); - return result; -} - -static int GeodSolve12() { - /* Check fix for inverse geodesics on extreme prolate/oblate - * ellipsoids Reported 2012-08-29 Stefan Guenther - * ; fixed 2012-10-07 */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, 89.8, -1.83); - geod_inverse(&g, 0, 0, -10, 160, &s12, &azi1, &azi2); - result += checkEquals(azi1, 120.27, 1e-2); - result += checkEquals(azi2, 105.15, 1e-2); - result += checkEquals(s12, 266.7, 1e-1); - return result; -} - -static int GeodSolve14() { - /* Check fix for inverse ignoring lon12 = nan */ - double azi1, azi2, s12, nan; - struct geod_geodesic g; - int result = 0; - { - double minus1 = -1; - /* cppcheck-suppress wrongmathcall */ - nan = sqrt(minus1); - } - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 0, 0, 1, nan, &s12, &azi1, &azi2); - result += checkNaN(azi1); - result += checkNaN(azi2); - result += checkNaN(s12); - return result; -} - -static int GeodSolve15() { - /* Initial implementation of Math::eatanhe was wrong for e^2 < 0. This - * checks that this is fixed. */ - double S12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, 6.4e6, -1/150.0); - geod_gendirect(&g, 1, 2, 3, 0, 4, - 0, 0, 0, 0, 0, 0, 0, &S12); - result += checkEquals(S12, 23700, 0.5); - return result; -} - -static int GeodSolve17() { - /* Check fix for LONG_UNROLL bug found on 2015-05-07 */ - double lat2, lon2, azi2; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - unsigned flags = GEOD_LONG_UNROLL; - geod_init(&g, wgs84_a, wgs84_f); - geod_gendirect(&g, 40, -75, -10, flags, 2e7, - &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, -39, 1); - result += checkEquals(lon2, -254, 1); - result += checkEquals(azi2, -170, 1); - geod_lineinit(&l, &g, 40, -75, -10, 0); - geod_genposition(&l, flags, 2e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, -39, 1); - result += checkEquals(lon2, -254, 1); - result += checkEquals(azi2, -170, 1); - geod_direct(&g, 40, -75, -10, 2e7, &lat2, &lon2, &azi2); - result += checkEquals(lat2, -39, 1); - result += checkEquals(lon2, 105, 1); - result += checkEquals(azi2, -170, 1); - geod_position(&l, 2e7, &lat2, &lon2, &azi2); - result += checkEquals(lat2, -39, 1); - result += checkEquals(lon2, 105, 1); - result += checkEquals(azi2, -170, 1); - return result; -} - -static int GeodSolve26() { - /* Check 0/0 problem with area calculation on sphere 2015-09-08 */ - double S12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, 6.4e6, 0); - geod_geninverse(&g, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, &S12); - result += checkEquals(S12, 49911046115.0, 0.5); - return result; -} - -static int GeodSolve28() { - /* Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in - * Java implementation fixed on 2015-05-19). */ - double a12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, 6.4e6, 0.1); - a12 = geod_gendirect(&g, 1, 2, 10, 0, 5e6, 0, 0, 0, 0, 0, 0, 0, 0); - result += checkEquals(a12, 48.55570690, 0.5e-8); - return result; -} - -static int GeodSolve33() { - /* Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave -- - * sind(-0.0) = +0.0 -- and in some version of Visual Studio -- - * fmod(-0.0, 360.0) = +0.0. */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00000, 0.5e-5); - result += checkEquals(azi2, 90.00000, 0.5e-5); - result += checkEquals(s12, 19926189, 0.5); - geod_inverse(&g, 0, 0, 0, 179.5, &s12, &azi1, &azi2); - result += checkEquals(azi1, 55.96650, 0.5e-5); - result += checkEquals(azi2, 124.03350, 0.5e-5); - result += checkEquals(s12, 19980862, 0.5); - geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 20003931, 0.5); - geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 19893357, 0.5); - geod_init(&g, 6.4e6, 0); - geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00000, 0.5e-5); - result += checkEquals(azi2, 90.00000, 0.5e-5); - result += checkEquals(s12, 19994492, 0.5); - geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 20106193, 0.5); - geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 19994492, 0.5); - geod_init(&g, 6.4e6, -1/300.0); - geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00000, 0.5e-5); - result += checkEquals(azi2, 90.00000, 0.5e-5); - result += checkEquals(s12, 19994492, 0.5); - geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 90.00000, 0.5e-5); - result += checkEquals(azi2, 90.00000, 0.5e-5); - result += checkEquals(s12, 20106193, 0.5); - geod_inverse(&g, 0, 0, 0.5, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 33.02493, 0.5e-5); - result += checkEquals(azi2, 146.97364, 0.5e-5); - result += checkEquals(s12, 20082617, 0.5); - geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 20027270, 0.5); - - return result; -} - -static int GeodSolve55() { - /* Check fix for nan + point on equator or pole not returning all nans in - * Geodesic::Inverse, found 2015-09-23. */ - double azi1, azi2, s12, nan; - struct geod_geodesic g; - int result = 0; - { - double minus1 = -1; - /* cppcheck-suppress wrongmathcall */ - nan = sqrt(minus1); - } - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, nan, 0, 0, 90, &s12, &azi1, &azi2); - result += checkNaN(azi1); - result += checkNaN(azi2); - result += checkNaN(s12); - geod_inverse(&g, nan, 0, 90, 9, &s12, &azi1, &azi2); - result += checkNaN(azi1); - result += checkNaN(azi2); - result += checkNaN(s12); - return result; -} - -static int GeodSolve59() { - /* Check for points close with longitudes close to 180 deg apart. */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 5, 0.00000000000001, 10, 180, &s12, &azi1, &azi2); - result += checkEquals(azi1, 0.000000000000035, 1.5e-14); - result += checkEquals(azi2, 179.99999999999996, 1.5e-14); - result += checkEquals(s12, 18345191.174332713, 5e-9); - return result; -} - -static int GeodSolve61() { - /* Make sure small negative azimuths are west-going */ - double lat2, lon2, azi2; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - unsigned flags = GEOD_LONG_UNROLL; - geod_init(&g, wgs84_a, wgs84_f); - geod_gendirect(&g, 45, 0, -0.000000000000000003, flags, 1e7, - &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, 45.30632, 0.5e-5); - result += checkEquals(lon2, -180, 0.5e-5); - result += checkEquals(fabs(azi2), 180, 0.5e-5); - geod_inverseline(&l, &g, 45, 0, 80, -0.000000000000000003, 0); - geod_genposition(&l, flags, 1e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, 45.30632, 0.5e-5); - result += checkEquals(lon2, -180, 0.5e-5); - result += checkEquals(fabs(azi2), 180, 0.5e-5); - return result; -} - -static int GeodSolve65() { - /* Check for bug in east-going check in GeodesicLine (needed to check for - * sign of 0) and sign error in area calculation due to a bogus override of - * the code for alp12. Found/fixed on 2015-12-19. */ - double lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - unsigned flags = GEOD_LONG_UNROLL, caps = GEOD_ALL; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverseline(&l, &g, 30, -0.000000000000000001, -31, 180, caps); - a12 = geod_genposition(&l, flags, 1e7, - &lat2, &lon2, &azi2, &s12, &m12, &M12, &M21, &S12); - result += checkEquals(lat2, -60.23169, 0.5e-5); - result += checkEquals(lon2, -0.00000, 0.5e-5); - result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); - result += checkEquals(s12, 10000000, 0.5); - result += checkEquals(a12, 90.06544, 0.5e-5); - result += checkEquals(m12, 6363636, 0.5); - result += checkEquals(M12, -0.0012834, 0.5e-7); - result += checkEquals(M21, 0.0013749, 0.5e-7); - result += checkEquals(S12, 0, 0.5); - a12 = geod_genposition(&l, flags, 2e7, - &lat2, &lon2, &azi2, &s12, &m12, &M12, &M21, &S12); - result += checkEquals(lat2, -30.03547, 0.5e-5); - result += checkEquals(lon2, -180.00000, 0.5e-5); - result += checkEquals(azi2, -0.00000, 0.5e-5); - result += checkEquals(s12, 20000000, 0.5); - result += checkEquals(a12, 179.96459, 0.5e-5); - result += checkEquals(m12, 54342, 0.5); - result += checkEquals(M12, -1.0045592, 0.5e-7); - result += checkEquals(M21, -0.9954339, 0.5e-7); - result += checkEquals(S12, 127516405431022.0, 0.5); - return result; -} - -static int GeodSolve67() { - /* Check for InverseLine if line is slightly west of S and that s13 is - correctly set. */ - double lat2, lon2, azi2; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - unsigned flags = GEOD_LONG_UNROLL; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverseline(&l, &g, -5, -0.000000000000002, -10, 180, 0); - geod_genposition(&l, flags, 2e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, 4.96445, 0.5e-5); - result += checkEquals(lon2, -180.00000, 0.5e-5); - result += checkEquals(azi2, -0.00000, 0.5e-5); - geod_genposition(&l, flags, 0.5 * l.s13, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); - result += checkEquals(lat2, -87.52461, 0.5e-5); - result += checkEquals(lon2, -0.00000, 0.5e-5); - result += checkEquals(azi2, -180.00000, 0.5e-5); - return result; -} - -static int GeodSolve71() { - /* Check that DirectLine sets s13. */ - double lat2, lon2, azi2; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_directline(&l, &g, 1, 2, 45, 1e7, 0); - geod_position(&l, 0.5 * l.s13, &lat2, &lon2, &azi2); - result += checkEquals(lat2, 30.92625, 0.5e-5); - result += checkEquals(lon2, 37.54640, 0.5e-5); - result += checkEquals(azi2, 55.43104, 0.5e-5); - return result; -} - -static int GeodSolve73() { - /* Check for backwards from the pole bug reported by Anon on 2016-02-13. - * This only affected the Java implementation. It was introduced in Java - * version 1.44 and fixed in 1.46-SNAPSHOT on 2016-01-17. */ - double lat2, lon2, azi2; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_direct(&g, 90, 10, 180, -1e6, - &lat2, &lon2, &azi2); - result += checkEquals(lat2, 81.04623, 0.5e-5); - result += checkEquals(lon2, -170, 0.5e-5); - result += azi2 == 0 ? 0 : 1; - result += 1/azi2 > 0 ? 0 : 1; /* Check that azi2 = +0.0 not -0.0 */ - return result; -} - -static void planimeter(const struct geod_geodesic* g, - double points[][2], int N, - double* perimeter, double* area) { - struct geod_polygon p; - int i; - geod_polygon_init(&p, 0); - for (i = 0; i < N; ++i) - geod_polygon_addpoint(g, &p, points[i][0], points[i][1]); - geod_polygon_compute(g, &p, 0, 1, area, perimeter); -} - -static void polylength(const struct geod_geodesic* g, - double points[][2], int N, - double* perimeter) { - struct geod_polygon p; - int i; - geod_polygon_init(&p, 1); - for (i = 0; i < N; ++i) - geod_polygon_addpoint(g, &p, points[i][0], points[i][1]); - geod_polygon_compute(g, &p, 0, 1, 0, perimeter); -} - -static int GeodSolve74() { - /* Check fix for inaccurate areas, bug introduced in v1.46, fixed - 2015-10-16. */ - double a12, s12, azi1, azi2, m12, M12, M21, S12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - a12 = geod_geninverse(&g, 54.1589, 15.3872, 54.1591, 15.3877, - &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); - result += checkEquals(azi1, 55.723110355, 5e-9); - result += checkEquals(azi2, 55.723515675, 5e-9); - result += checkEquals(s12, 39.527686385, 5e-9); - result += checkEquals(a12, 0.000355495, 5e-9); - result += checkEquals(m12, 39.527686385, 5e-9); - result += checkEquals(M12, 0.999999995, 5e-9); - result += checkEquals(M21, 0.999999995, 5e-9); - result += checkEquals(S12, 286698586.30197, 5e-4); - return result; -} - -static int GeodSolve76() { - /* The distance from Wellington and Salamanca (a classic failure of - Vincenty) */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, -(41+19/60.0), 174+49/60.0, 40+58/60.0, -(5+30/60.0), - &s12, &azi1, &azi2); - result += checkEquals(azi1, 160.39137649664, 0.5e-11); - result += checkEquals(azi2, 19.50042925176, 0.5e-11); - result += checkEquals(s12, 19960543.857179, 0.5e-6); - return result; -} - -static int GeodSolve78() { - /* An example where the NGS calculator fails to converge */ - double azi1, azi2, s12; - struct geod_geodesic g; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_inverse(&g, 27.2, 0.0, -27.1, 179.5, &s12, &azi1, &azi2); - result += checkEquals(azi1, 45.82468716758, 0.5e-11); - result += checkEquals(azi2, 134.22776532670, 0.5e-11); - result += checkEquals(s12, 19974354.765767, 0.5e-6); - return result; -} - -static int GeodSolve80() { - /* Some tests to add code coverage: computing scale in special cases + zero - length geodesic (includes GeodSolve80 - GeodSolve83) + using an incapable - line. */ - double a12, s12, azi1, azi2, m12, M12, M21, S12; - struct geod_geodesic g; - struct geod_geodesicline l; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_geninverse(&g, 0, 0, 0, 90, 0, 0, 0, 0, &M12, &M21, 0); - result += checkEquals(M12, -0.00528427534, 0.5e-10); - result += checkEquals(M21, -0.00528427534, 0.5e-10); - geod_geninverse(&g, 0, 0, 1e-6, 1e-6, 0, 0, 0, 0, &M12, &M21, 0); - result += checkEquals(M12, 1, 0.5e-10); - result += checkEquals(M21, 1, 0.5e-10); - a12 = geod_geninverse(&g, 20.001, 0, 20.001, 0, - &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); - result += checkEquals(a12, 0, 1e-13); - result += checkEquals(s12, 0, 1e-8); - result += checkEquals(azi1, 180, 1e-13); - result += checkEquals(azi2, 180, 1e-13); - result += checkEquals(m12, 0, 1e-8); - result += checkEquals(M12, 1, 1e-15); - result += checkEquals(M21, 1, 1e-15); - result += checkEquals(S12, 0, 1e-10); - a12 = geod_geninverse(&g, 90, 0, 90, 180, - &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); - result += checkEquals(a12, 0, 1e-13); - result += checkEquals(s12, 0, 1e-8); - result += checkEquals(azi1, 0, 1e-13); - result += checkEquals(azi2, 180, 1e-13); - result += checkEquals(m12, 0, 1e-8); - result += checkEquals(M12, 1, 1e-15); - result += checkEquals(M21, 1, 1e-15); - result += checkEquals(S12, 127516405431022, 0.5); - /* An incapable line which can't take distance as input */ - geod_lineinit(&l, &g, 1, 2, 90, GEOD_LATITUDE); - a12 = geod_genposition(&l, 0, 1000, 0, 0, 0, 0, 0, 0, 0, 0); - result += checkNaN(a12); - return result; -} - -static int Planimeter0() { - /* Check fix for pole-encircling bug found 2011-03-16 */ - double pa[4][2] = {{89, 0}, {89, 90}, {89, 180}, {89, 270}}; - double pb[4][2] = {{-89, 0}, {-89, 90}, {-89, 180}, {-89, 270}}; - double pc[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; - double pd[3][2] = {{90, 0}, {0, 0}, {0, 90}}; - struct geod_geodesic g; - double perimeter, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - - planimeter(&g, pa, 4, &perimeter, &area); - result += checkEquals(perimeter, 631819.8745, 1e-4); - result += checkEquals(area, 24952305678.0, 1); - - planimeter(&g, pb, 4, &perimeter, &area); - result += checkEquals(perimeter, 631819.8745, 1e-4); - result += checkEquals(area, -24952305678.0, 1); - - planimeter(&g, pc, 4, &perimeter, &area); - result += checkEquals(perimeter, 627598.2731, 1e-4); - result += checkEquals(area, 24619419146.0, 1); - - planimeter(&g, pd, 3, &perimeter, &area); - result += checkEquals(perimeter, 30022685, 1); - result += checkEquals(area, 63758202715511.0, 1); - - polylength(&g, pd, 3, &perimeter); - result += checkEquals(perimeter, 20020719, 1); - - return result; -} - -static int Planimeter5() { - /* Check fix for Planimeter pole crossing bug found 2011-06-24 */ - double points[3][2] = {{89, 0.1}, {89, 90.1}, {89, -179.9}}; - struct geod_geodesic g; - double perimeter, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - planimeter(&g, points, 3, &perimeter, &area); - result += checkEquals(perimeter, 539297, 1); - result += checkEquals(area, 12476152838.5, 1); - return result; -} - -static int Planimeter6() { - /* Check fix for Planimeter lon12 rounding bug found 2012-12-03 */ - double pa[3][2] = {{9, -0.00000000000001}, {9, 180}, {9, 0}}; - double pb[3][2] = {{9, 0.00000000000001}, {9, 0}, {9, 180}}; - double pc[3][2] = {{9, 0.00000000000001}, {9, 180}, {9, 0}}; - double pd[3][2] = {{9, -0.00000000000001}, {9, 0}, {9, 180}}; - struct geod_geodesic g; - double perimeter, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - - planimeter(&g, pa, 3, &perimeter, &area); - result += checkEquals(perimeter, 36026861, 1); - result += checkEquals(area, 0, 1); - planimeter(&g, pb, 3, &perimeter, &area); - result += checkEquals(perimeter, 36026861, 1); - result += checkEquals(area, 0, 1); - planimeter(&g, pc, 3, &perimeter, &area); - result += checkEquals(perimeter, 36026861, 1); - result += checkEquals(area, 0, 1); - planimeter(&g, pd, 3, &perimeter, &area); - result += checkEquals(perimeter, 36026861, 1); - result += checkEquals(area, 0, 1); - return result; -} - -static int Planimeter12() { - /* Area of arctic circle (not really -- adjunct to rhumb-area test) */ - double points[2][2] = {{66.562222222, 0}, {66.562222222, 180}}; - struct geod_geodesic g; - double perimeter, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - planimeter(&g, points, 2, &perimeter, &area); - result += checkEquals(perimeter, 10465729, 1); - result += checkEquals(area, 0, 1); - return result; -} - -static int Planimeter13() { - /* Check encircling pole twice */ - double points[6][2] = {{89,-360}, {89,-240}, {89,-120}, - {89,0}, {89,120}, {89,240}}; - struct geod_geodesic g; - double perimeter, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - planimeter(&g, points, 6, &perimeter, &area); - result += checkEquals(perimeter, 1160741, 1); - result += checkEquals(area, 32415230256.0, 1); - return result; -} - -static int Planimeter15() { - /* Coverage tests, includes Planimeter15 - Planimeter18 (combinations of - reverse and sign) + calls to testpoint, testedge, geod_polygonarea. */ - struct geod_geodesic g; - struct geod_polygon p; - double lat[] = {2, 1, 3}, lon[] = {1, 2, 3}; - double area, s12, azi1; - double r = 18454562325.45119, - a0 = 510065621724088.5093; /* ellipsoid area */ - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_polygon_init(&p, 0); - geod_polygon_addpoint(&g, &p, lat[0], lon[0]); - geod_polygon_addpoint(&g, &p, lat[1], lon[1]); - geod_polygon_testpoint(&g, &p, lat[2], lon[2], 0, 1, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_testpoint(&g, &p, lat[2], lon[2], 0, 0, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_testpoint(&g, &p, lat[2], lon[2], 1, 1, &area, 0); - result += checkEquals(area, -r, 0.5); - geod_polygon_testpoint(&g, &p, lat[2], lon[2], 1, 0, &area, 0); - result += checkEquals(area, a0-r, 0.5); - geod_inverse(&g, lat[1], lon[1], lat[2], lon[2], &s12, &azi1, 0); - geod_polygon_testedge(&g, &p, azi1, s12, 0, 1, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_testedge(&g, &p, azi1, s12, 0, 0, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_testedge(&g, &p, azi1, s12, 1, 1, &area, 0); - result += checkEquals(area, -r, 0.5); - geod_polygon_testedge(&g, &p, azi1, s12, 1, 0, &area, 0); - result += checkEquals(area, a0-r, 0.5); - geod_polygon_addpoint(&g, &p, lat[2], lon[2]); - geod_polygon_compute(&g, &p, 0, 1, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_compute(&g, &p, 0, 0, &area, 0); - result += checkEquals(area, r, 0.5); - geod_polygon_compute(&g, &p, 1, 1, &area, 0); - result += checkEquals(area, -r, 0.5); - geod_polygon_compute(&g, &p, 1, 0, &area, 0); - result += checkEquals(area, a0-r, 0.5); - geod_polygonarea(&g, lat, lon, 3, &area, 0); - result += checkEquals(area, r, 0.5); - return result; -} - -static int Planimeter19() { - /* Coverage tests, includes Planimeter19 - Planimeter20 (degenerate - polygons) + extra cases. */ - struct geod_geodesic g; - struct geod_polygon p; - double area, perim; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_polygon_init(&p, 0); - geod_polygon_compute(&g, &p, 0, 1, &area, &perim); - result += area == 0 ? 0 : 1; - result += perim == 0 ? 0 : 1; - geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, &area, &perim); - result += area == 0 ? 0 : 1; - result += perim == 0 ? 0 : 1; - geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, &area, &perim); - result += checkNaN(area); - result += checkNaN(perim); - geod_polygon_addpoint(&g, &p, 1, 1); - geod_polygon_compute(&g, &p, 0, 1, &area, &perim); - result += area == 0 ? 0 : 1; - result += perim == 0 ? 0 : 1; - geod_polygon_init(&p, 1); - geod_polygon_compute(&g, &p, 0, 1, 0, &perim); - result += perim == 0 ? 0 : 1; - geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, 0, &perim); - result += perim == 0 ? 0 : 1; - geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); - result += checkNaN(perim); - geod_polygon_addpoint(&g, &p, 1, 1); - geod_polygon_compute(&g, &p, 0, 1, 0, &perim); - result += perim == 0 ? 0 : 1; - return result; -} - -static int Planimeter21() { - /* Some test to add code coverage: multiple circlings of pole (includes - Planimeter21 - Planimeter28) + invocations via testpoint and testedge. - Some of the results for i = 4 in the loop are wrong because we don't - reduce the area to the allowed range correctly. However these cases are - not "simple" polygons, so we'll defer fixing the problem for now. - */ - struct geod_geodesic g; - struct geod_polygon p; - double area, lat = 45, - a = 39.2144607176828184218, s = 8420705.40957178156285, - r = 39433884866571.4277, /* Area for one circuit */ - a0 = 510065621724088.5093; /* Ellipsoid area */ - int result = 0, i; - geod_init(&g, wgs84_a, wgs84_f); - geod_polygon_init(&p, 0); - geod_polygon_addpoint(&g, &p, lat, 60); - geod_polygon_addpoint(&g, &p, lat, 180); - geod_polygon_addpoint(&g, &p, lat, -60); - geod_polygon_addpoint(&g, &p, lat, 60); - geod_polygon_addpoint(&g, &p, lat, 180); - geod_polygon_addpoint(&g, &p, lat, -60); - for (i = 3; i <= 4; ++i) { - geod_polygon_addpoint(&g, &p, lat, 60); - geod_polygon_addpoint(&g, &p, lat, 180); - geod_polygon_testpoint(&g, &p, lat, -60, 0, 1, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_testpoint(&g, &p, lat, -60, 0, 0, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_testpoint(&g, &p, lat, -60, 1, 1, &area, 0); - if (i != 4) result += checkEquals(area, -i*r, 0.5); - geod_polygon_testpoint(&g, &p, lat, -60, 1, 0, &area, 0); - result += checkEquals(area, -i*r + a0, 0.5); - geod_polygon_testedge(&g, &p, a, s, 0, 1, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_testedge(&g, &p, a, s, 0, 0, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_testedge(&g, &p, a, s, 1, 1, &area, 0); - if (i != 4) result += checkEquals(area, -i*r, 0.5); - geod_polygon_testedge(&g, &p, a, s, 1, 0, &area, 0); - result += checkEquals(area, -i*r + a0, 0.5); - geod_polygon_addpoint(&g, &p, lat, -60); - geod_polygon_compute(&g, &p, 0, 1, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_compute(&g, &p, 0, 0, &area, 0); - if (i != 4) result += checkEquals(area, i*r, 0.5); - geod_polygon_compute(&g, &p, 1, 1, &area, 0); - if (i != 4) result += checkEquals(area, -i*r, 0.5); - geod_polygon_compute(&g, &p, 1, 0, &area, 0); - result += checkEquals(area, -i*r + a0, 0.5); - } - return result; -} - -static int AddEdge1() { - /* Check fix to transitdirect vs transit zero handling inconsistency */ - struct geod_geodesic g; - struct geod_polygon p; - double area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_polygon_init(&p, 0); - geod_polygon_addpoint(&g, &p, 0, 0); - geod_polygon_addedge(&g, &p, 90, 1000); - geod_polygon_addedge(&g, &p, 0, 1000); - geod_polygon_addedge(&g, &p, -90, 1000); - geod_polygon_compute(&g, &p, 0, 1, &area, 0); - result += checkEquals(area, 1000000.0, 0.01); - return result; -} - -static int EmptyPoly() { - struct geod_geodesic g; - struct geod_polygon p; - double perim, area; - int result = 0; - geod_init(&g, wgs84_a, wgs84_f); - geod_polygon_init(&p, 0); - geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, &area, &perim); - result += area == 0 ? 0 : 1; - result += perim == 0 ? 0 : 1; - geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, &area, &perim); - result += checkNaN(area); - result += checkNaN(perim); - geod_polygon_compute(&g, &p, 0, 1, &area, &perim); - result += area == 0 ? 0 : 1; - result += perim == 0 ? 0 : 1; - geod_polygon_init(&p, 1); - geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, 0, &perim); - result += perim == 0 ? 0 : 1; - geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); - result += checkNaN(perim); - geod_polygon_compute(&g, &p, 0, 1, 0, &perim); - result += perim == 0 ? 0 : 1; - geod_polygon_addpoint(&g, &p, 1, 1); - geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); - result += checkEquals(perim, 1000, 1e-10); - geod_polygon_testpoint(&g, &p, 2, 2, 0, 1, 0, &perim); - result += checkEquals(perim, 156876.149, 0.5e-3); - return result; -} - -int main() { - int n = 0, i; - if ((i = testinverse())) {++n; printf("testinverse fail: %d\n", i);} - if ((i = testdirect())) {++n; printf("testdirect fail: %d\n", i);} - if ((i = testarcdirect())) {++n; printf("testarcdirect fail: %d\n", i);} - if ((i = GeodSolve0())) {++n; printf("GeodSolve0 fail: %d\n", i);} - if ((i = GeodSolve1())) {++n; printf("GeodSolve1 fail: %d\n", i);} - if ((i = GeodSolve2())) {++n; printf("GeodSolve2 fail: %d\n", i);} - if ((i = GeodSolve4())) {++n; printf("GeodSolve4 fail: %d\n", i);} - if ((i = GeodSolve5())) {++n; printf("GeodSolve5 fail: %d\n", i);} - if ((i = GeodSolve6())) {++n; printf("GeodSolve6 fail: %d\n", i);} - if ((i = GeodSolve9())) {++n; printf("GeodSolve9 fail: %d\n", i);} - if ((i = GeodSolve10())) {++n; printf("GeodSolve10 fail: %d\n", i);} - if ((i = GeodSolve11())) {++n; printf("GeodSolve11 fail: %d\n", i);} - if ((i = GeodSolve12())) {++n; printf("GeodSolve12 fail: %d\n", i);} - if ((i = GeodSolve14())) {++n; printf("GeodSolve14 fail: %d\n", i);} - if ((i = GeodSolve15())) {++n; printf("GeodSolve15 fail: %d\n", i);} - if ((i = GeodSolve17())) {++n; printf("GeodSolve17 fail: %d\n", i);} - if ((i = GeodSolve26())) {++n; printf("GeodSolve26 fail: %d\n", i);} - if ((i = GeodSolve28())) {++n; printf("GeodSolve28 fail: %d\n", i);} - if ((i = GeodSolve33())) {++n; printf("GeodSolve33 fail: %d\n", i);} - if ((i = GeodSolve55())) {++n; printf("GeodSolve55 fail: %d\n", i);} - if ((i = GeodSolve59())) {++n; printf("GeodSolve59 fail: %d\n", i);} - if ((i = GeodSolve61())) {++n; printf("GeodSolve61 fail: %d\n", i);} - if ((i = GeodSolve65())) {++n; printf("GeodSolve65 fail: %d\n", i);} - if ((i = GeodSolve67())) {++n; printf("GeodSolve67 fail: %d\n", i);} - if ((i = GeodSolve71())) {++n; printf("GeodSolve71 fail: %d\n", i);} - if ((i = GeodSolve73())) {++n; printf("GeodSolve73 fail: %d\n", i);} - if ((i = GeodSolve74())) {++n; printf("GeodSolve74 fail: %d\n", i);} - if ((i = GeodSolve76())) {++n; printf("GeodSolve76 fail: %d\n", i);} - if ((i = GeodSolve78())) {++n; printf("GeodSolve78 fail: %d\n", i);} - if ((i = GeodSolve80())) {++n; printf("GeodSolve80 fail: %d\n", i);} - if ((i = Planimeter0())) {++n; printf("Planimeter0 fail: %d\n", i);} - if ((i = Planimeter5())) {++n; printf("Planimeter5 fail: %d\n", i);} - if ((i = Planimeter6())) {++n; printf("Planimeter6 fail: %d\n", i);} - if ((i = Planimeter12())) {++n; printf("Planimeter12 fail: %d\n", i);} - if ((i = Planimeter13())) {++n; printf("Planimeter13 fail: %d\n", i);} - if ((i = Planimeter15())) {++n; printf("Planimeter15 fail: %d\n", i);} - if ((i = Planimeter19())) {++n; printf("Planimeter19 fail: %d\n", i);} - if ((i = Planimeter21())) {++n; printf("Planimeter21 fail: %d\n", i);} - if ((i = AddEdge1())) {++n; printf("AddEdge1 fail: %d\n", i);} - if ((i = EmptyPoly())) {++n; printf("EmptyPoly fail: %d\n", i);} - return n; -} - -/** @endcond */ diff --git a/src/geodtest.cpp b/src/geodtest.cpp new file mode 100644 index 00000000..0ee86d5c --- /dev/null +++ b/src/geodtest.cpp @@ -0,0 +1,1069 @@ +/** + * \file geodtest.c + * \brief Test suite for the geodesic routines in C + * + * Run these tests by configuring with cmake and running "make test". + * + * Copyright (c) Charles Karney (2015-2018) and licensed + * under the MIT/X11 License. For more information, see + * https://geographiclib.sourceforge.io/ + **********************************************************************/ + +/** @cond SKIP */ + +#include "geodesic.h" +#include +#include + +#if defined(_MSC_VER) +/* Squelch warnings about assignment within conditional expression */ +# pragma warning (disable: 4706) +#endif + +static const double wgs84_a = 6378137, wgs84_f = 1/298.257223563; /* WGS84 */ + +static int checkEquals(double x, double y, double d) { + if (fabs(x - y) <= d) + return 0; + printf("checkEquals fails: %.7g != %.7g +/- %.7g\n", x, y, d); + return 1; +} + +static int checkNaN(double x) { + if (x != x) + return 0; + printf("checkNaN fails: %.7g\n", x); + return 1; +} + +static const int ncases = 20; +static const double testcases[20][12] = { + {35.60777, -139.44815, 111.098748429560326, + -11.17491, -69.95921, 129.289270889708762, + 8935244.5604818305, 80.50729714281974, 6273170.2055303837, + 0.16606318447386067, 0.16479116945612937, 12841384694976.432}, + {55.52454, 106.05087, 22.020059880982801, + 77.03196, 197.18234, 109.112041110671519, + 4105086.1713924406, 36.892740690445894, 3828869.3344387607, + 0.80076349608092607, 0.80101006984201008, 61674961290615.615}, + {-21.97856, 142.59065, -32.44456876433189, + 41.84138, 98.56635, -41.84359951440466, + 8394328.894657671, 75.62930491011522, 6161154.5773110616, + 0.24816339233950381, 0.24930251203627892, -6637997720646.717}, + {-66.99028, 112.2363, 173.73491240878403, + -12.70631, 285.90344, 2.512956620913668, + 11150344.2312080241, 100.278634181155759, 6289939.5670446687, + -0.17199490274700385, -0.17722569526345708, -121287239862139.744}, + {-17.42761, 173.34268, -159.033557661192928, + -15.84784, 5.93557, -20.787484651536988, + 16076603.1631180673, 144.640108810286253, 3732902.1583877189, + -0.81273638700070476, -0.81299800519154474, 97825992354058.708}, + {32.84994, 48.28919, 150.492927788121982, + -56.28556, 202.29132, 48.113449399816759, + 16727068.9438164461, 150.565799985466607, 3147838.1910180939, + -0.87334918086923126, -0.86505036767110637, -72445258525585.010}, + {6.96833, 52.74123, 92.581585386317712, + -7.39675, 206.17291, 90.721692165923907, + 17102477.2496958388, 154.147366239113561, 2772035.6169917581, + -0.89991282520302447, -0.89986892177110739, -1311796973197.995}, + {-50.56724, -16.30485, -105.439679907590164, + -33.56571, -94.97412, -47.348547835650331, + 6455670.5118668696, 58.083719495371259, 5409150.7979815838, + 0.53053508035997263, 0.52988722644436602, 41071447902810.047}, + {-58.93002, -8.90775, 140.965397902500679, + -8.91104, 133.13503, 19.255429433416599, + 11756066.0219864627, 105.755691241406877, 6151101.2270708536, + -0.26548622269867183, -0.27068483874510741, -86143460552774.735}, + {-68.82867, -74.28391, 93.774347763114881, + -50.63005, -8.36685, 34.65564085411343, + 3956936.926063544, 35.572254987389284, 3708890.9544062657, + 0.81443963736383502, 0.81420859815358342, -41845309450093.787}, + {-10.62672, -32.0898, -86.426713286747751, + 5.883, -134.31681, -80.473780971034875, + 11470869.3864563009, 103.387395634504061, 6184411.6622659713, + -0.23138683500430237, -0.23155097622286792, 4198803992123.548}, + {-21.76221, 166.90563, 29.319421206936428, + 48.72884, 213.97627, 43.508671946410168, + 9098627.3986554915, 81.963476716121964, 6299240.9166992283, + 0.13965943368590333, 0.14152969707656796, 10024709850277.476}, + {-19.79938, -174.47484, 71.167275780171533, + -11.99349, -154.35109, 65.589099775199228, + 2319004.8601169389, 20.896611684802389, 2267960.8703918325, + 0.93427001867125849, 0.93424887135032789, -3935477535005.785}, + {-11.95887, -116.94513, 92.712619830452549, + 4.57352, 7.16501, 78.64960934409585, + 13834722.5801401374, 124.688684161089762, 5228093.177931598, + -0.56879356755666463, -0.56918731952397221, -9919582785894.853}, + {-87.85331, 85.66836, -65.120313040242748, + 66.48646, 16.09921, -4.888658719272296, + 17286615.3147144645, 155.58592449699137, 2635887.4729110181, + -0.90697975771398578, -0.91095608883042767, 42667211366919.534}, + {1.74708, 128.32011, -101.584843631173858, + -11.16617, 11.87109, -86.325793296437476, + 12942901.1241347408, 116.650512484301857, 5682744.8413270572, + -0.44857868222697644, -0.44824490340007729, 10763055294345.653}, + {-25.72959, -144.90758, -153.647468693117198, + -57.70581, -269.17879, -48.343983158876487, + 9413446.7452453107, 84.664533838404295, 6356176.6898881281, + 0.09492245755254703, 0.09737058264766572, 74515122850712.444}, + {-41.22777, 122.32875, 14.285113402275739, + -7.57291, 130.37946, 10.805303085187369, + 3812686.035106021, 34.34330804743883, 3588703.8812128856, + 0.82605222593217889, 0.82572158200920196, -2456961531057.857}, + {11.01307, 138.25278, 79.43682622782374, + 6.62726, 247.05981, 103.708090215522657, + 11911190.819018408, 107.341669954114577, 6070904.722786735, + -0.29767608923657404, -0.29785143390252321, 17121631423099.696}, + {-29.47124, 95.14681, -163.779130441688382, + -27.46601, -69.15955, -15.909335945554969, + 13487015.8381145492, 121.294026715742277, 5481428.9945736388, + -0.51527225545373252, -0.51556587964721788, 104679964020340.318}}; + +static int testinverse() { + double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; + double azi1a, azi2a, s12a, a12a, m12a, M12a, M21a, S12a; + struct geod_geodesic g; + int i, result = 0; + geod_init(&g, wgs84_a, wgs84_f); + for (i = 0; i < ncases; ++i) { + lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; + lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; + s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; + M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; + a12a = geod_geninverse(&g, lat1, lon1, lat2, lon2, &s12a, &azi1a, &azi2a, + &m12a, &M12a, &M21a, &S12a); + result += checkEquals(azi1, azi1a, 1e-13); + result += checkEquals(azi2, azi2a, 1e-13); + result += checkEquals(s12, s12a, 1e-8); + result += checkEquals(a12, a12a, 1e-13); + result += checkEquals(m12, m12a, 1e-8); + result += checkEquals(M12, M12a, 1e-15); + result += checkEquals(M21, M21a, 1e-15); + result += checkEquals(S12, S12a, 0.1); + } + return result; +} + +static int testdirect() { + double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; + double lat2a, lon2a, azi2a, a12a, m12a, M12a, M21a, S12a; + struct geod_geodesic g; + int i, result = 0; + unsigned flags = GEOD_LONG_UNROLL; + geod_init(&g, wgs84_a, wgs84_f); + for (i = 0; i < ncases; ++i) { + lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; + lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; + s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; + M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; + a12a = geod_gendirect(&g, lat1, lon1, azi1, flags, s12, + &lat2a, &lon2a, &azi2a, 0, + &m12a, &M12a, &M21a, &S12a); + result += checkEquals(lat2, lat2a, 1e-13); + result += checkEquals(lon2, lon2a, 1e-13); + result += checkEquals(azi2, azi2a, 1e-13); + result += checkEquals(a12, a12a, 1e-13); + result += checkEquals(m12, m12a, 1e-8); + result += checkEquals(M12, M12a, 1e-15); + result += checkEquals(M21, M21a, 1e-15); + result += checkEquals(S12, S12a, 0.1); + } + return result; +} + +static int testarcdirect() { + double lat1, lon1, azi1, lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; + double lat2a, lon2a, azi2a, s12a, m12a, M12a, M21a, S12a; + struct geod_geodesic g; + int i, result = 0; + unsigned flags = GEOD_ARCMODE | GEOD_LONG_UNROLL; + geod_init(&g, wgs84_a, wgs84_f); + for (i = 0; i < ncases; ++i) { + lat1 = testcases[i][0]; lon1 = testcases[i][1]; azi1 = testcases[i][2]; + lat2 = testcases[i][3]; lon2 = testcases[i][4]; azi2 = testcases[i][5]; + s12 = testcases[i][6]; a12 = testcases[i][7]; m12 = testcases[i][8]; + M12 = testcases[i][9]; M21 = testcases[i][10]; S12 = testcases[i][11]; + geod_gendirect(&g, lat1, lon1, azi1, flags, a12, + &lat2a, &lon2a, &azi2a, &s12a, &m12a, &M12a, &M21a, &S12a); + result += checkEquals(lat2, lat2a, 1e-13); + result += checkEquals(lon2, lon2a, 1e-13); + result += checkEquals(azi2, azi2a, 1e-13); + result += checkEquals(s12, s12a, 1e-8); + result += checkEquals(m12, m12a, 1e-8); + result += checkEquals(M12, M12a, 1e-15); + result += checkEquals(M21, M21a, 1e-15); + result += checkEquals(S12, S12a, 0.1); + } + return result; +} + +static int GeodSolve0() { + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 40.6, -73.8, 49.01666667, 2.55, &s12, &azi1, &azi2); + result += checkEquals(azi1, 53.47022, 0.5e-5); + result += checkEquals(azi2, 111.59367, 0.5e-5); + result += checkEquals(s12, 5853226, 0.5); + return result; +} + +static int GeodSolve1() { + double lat2, lon2, azi2; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_direct(&g, 40.63972222, -73.77888889, 53.5, 5850e3, + &lat2, &lon2, &azi2); + result += checkEquals(lat2, 49.01467, 0.5e-5); + result += checkEquals(lon2, 2.56106, 0.5e-5); + result += checkEquals(azi2, 111.62947, 0.5e-5); + return result; +} + +static int GeodSolve2() { + /* Check fix for antipodal prolate bug found 2010-09-04 */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, 6.4e6, -1/150.0); + geod_inverse(&g, 0.07476, 0, -0.07476, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00078, 0.5e-5); + result += checkEquals(azi2, 90.00078, 0.5e-5); + result += checkEquals(s12, 20106193, 0.5); + geod_inverse(&g, 0.1, 0, -0.1, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00105, 0.5e-5); + result += checkEquals(azi2, 90.00105, 0.5e-5); + result += checkEquals(s12, 20106193, 0.5); + return result; +} + +static int GeodSolve4() { + /* Check fix for short line bug found 2010-05-21 */ + double s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 36.493349428792, 0, 36.49334942879201, .0000008, + &s12, 0, 0); + result += checkEquals(s12, 0.072, 0.5e-3); + return result; +} + +static int GeodSolve5() { + /* Check fix for point2=pole bug found 2010-05-03 */ + double lat2, lon2, azi2; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_direct(&g, 0.01777745589997, 30, 0, 10e6, &lat2, &lon2, &azi2); + result += checkEquals(lat2, 90, 0.5e-5); + if (lon2 < 0) { + result += checkEquals(lon2, -150, 0.5e-5); + result += checkEquals(fabs(azi2), 180, 0.5e-5); + } else { + result += checkEquals(lon2, 30, 0.5e-5); + result += checkEquals(azi2, 0, 0.5e-5); + } + return result; +} + +static int GeodSolve6() { + /* Check fix for volatile sbet12a bug found 2011-06-25 (gcc 4.4.4 + * x86 -O3). Found again on 2012-03-27 with tdm-mingw32 (g++ 4.6.1). */ + double s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 88.202499451857, 0, + -88.202499451857, 179.981022032992859592, &s12, 0, 0); + result += checkEquals(s12, 20003898.214, 0.5e-3); + geod_inverse(&g, 89.262080389218, 0, + -89.262080389218, 179.992207982775375662, &s12, 0, 0); + result += checkEquals(s12, 20003925.854, 0.5e-3); + geod_inverse(&g, 89.333123580033, 0, + -89.333123580032997687, 179.99295812360148422, &s12, 0, 0); + result += checkEquals(s12, 20003926.881, 0.5e-3); + return result; +} + +static int GeodSolve9() { + /* Check fix for volatile x bug found 2011-06-25 (gcc 4.4.4 x86 -O3) */ + double s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 56.320923501171, 0, + -56.320923501171, 179.664747671772880215, &s12, 0, 0); + result += checkEquals(s12, 19993558.287, 0.5e-3); + return result; +} + +static int GeodSolve10() { + /* Check fix for adjust tol1_ bug found 2011-06-25 (Visual Studio + * 10 rel + debug) */ + double s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 52.784459512564, 0, + -52.784459512563990912, 179.634407464943777557, &s12, 0, 0); + result += checkEquals(s12, 19991596.095, 0.5e-3); + return result; +} + +static int GeodSolve11() { + /* Check fix for bet2 = -bet1 bug found 2011-06-25 (Visual Studio + * 10 rel + debug) */ + double s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 48.522876735459, 0, + -48.52287673545898293, 179.599720456223079643, &s12, 0, 0); + result += checkEquals(s12, 19989144.774, 0.5e-3); + return result; +} + +static int GeodSolve12() { + /* Check fix for inverse geodesics on extreme prolate/oblate + * ellipsoids Reported 2012-08-29 Stefan Guenther + * ; fixed 2012-10-07 */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, 89.8, -1.83); + geod_inverse(&g, 0, 0, -10, 160, &s12, &azi1, &azi2); + result += checkEquals(azi1, 120.27, 1e-2); + result += checkEquals(azi2, 105.15, 1e-2); + result += checkEquals(s12, 266.7, 1e-1); + return result; +} + +static int GeodSolve14() { + /* Check fix for inverse ignoring lon12 = nan */ + double azi1, azi2, s12, nan; + struct geod_geodesic g; + int result = 0; + { + double minus1 = -1; + /* cppcheck-suppress wrongmathcall */ + nan = sqrt(minus1); + } + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 0, 0, 1, nan, &s12, &azi1, &azi2); + result += checkNaN(azi1); + result += checkNaN(azi2); + result += checkNaN(s12); + return result; +} + +static int GeodSolve15() { + /* Initial implementation of Math::eatanhe was wrong for e^2 < 0. This + * checks that this is fixed. */ + double S12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, 6.4e6, -1/150.0); + geod_gendirect(&g, 1, 2, 3, 0, 4, + 0, 0, 0, 0, 0, 0, 0, &S12); + result += checkEquals(S12, 23700, 0.5); + return result; +} + +static int GeodSolve17() { + /* Check fix for LONG_UNROLL bug found on 2015-05-07 */ + double lat2, lon2, azi2; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + unsigned flags = GEOD_LONG_UNROLL; + geod_init(&g, wgs84_a, wgs84_f); + geod_gendirect(&g, 40, -75, -10, flags, 2e7, + &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, -39, 1); + result += checkEquals(lon2, -254, 1); + result += checkEquals(azi2, -170, 1); + geod_lineinit(&l, &g, 40, -75, -10, 0); + geod_genposition(&l, flags, 2e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, -39, 1); + result += checkEquals(lon2, -254, 1); + result += checkEquals(azi2, -170, 1); + geod_direct(&g, 40, -75, -10, 2e7, &lat2, &lon2, &azi2); + result += checkEquals(lat2, -39, 1); + result += checkEquals(lon2, 105, 1); + result += checkEquals(azi2, -170, 1); + geod_position(&l, 2e7, &lat2, &lon2, &azi2); + result += checkEquals(lat2, -39, 1); + result += checkEquals(lon2, 105, 1); + result += checkEquals(azi2, -170, 1); + return result; +} + +static int GeodSolve26() { + /* Check 0/0 problem with area calculation on sphere 2015-09-08 */ + double S12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, 6.4e6, 0); + geod_geninverse(&g, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, &S12); + result += checkEquals(S12, 49911046115.0, 0.5); + return result; +} + +static int GeodSolve28() { + /* Check for bad placement of assignment of r.a12 with |f| > 0.01 (bug in + * Java implementation fixed on 2015-05-19). */ + double a12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, 6.4e6, 0.1); + a12 = geod_gendirect(&g, 1, 2, 10, 0, 5e6, 0, 0, 0, 0, 0, 0, 0, 0); + result += checkEquals(a12, 48.55570690, 0.5e-8); + return result; +} + +static int GeodSolve33() { + /* Check max(-0.0,+0.0) issues 2015-08-22 (triggered by bugs in Octave -- + * sind(-0.0) = +0.0 -- and in some version of Visual Studio -- + * fmod(-0.0, 360.0) = +0.0. */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00000, 0.5e-5); + result += checkEquals(azi2, 90.00000, 0.5e-5); + result += checkEquals(s12, 19926189, 0.5); + geod_inverse(&g, 0, 0, 0, 179.5, &s12, &azi1, &azi2); + result += checkEquals(azi1, 55.96650, 0.5e-5); + result += checkEquals(azi2, 124.03350, 0.5e-5); + result += checkEquals(s12, 19980862, 0.5); + geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 20003931, 0.5); + geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 19893357, 0.5); + geod_init(&g, 6.4e6, 0); + geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00000, 0.5e-5); + result += checkEquals(azi2, 90.00000, 0.5e-5); + result += checkEquals(s12, 19994492, 0.5); + geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 20106193, 0.5); + geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 19994492, 0.5); + geod_init(&g, 6.4e6, -1/300.0); + geod_inverse(&g, 0, 0, 0, 179, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00000, 0.5e-5); + result += checkEquals(azi2, 90.00000, 0.5e-5); + result += checkEquals(s12, 19994492, 0.5); + geod_inverse(&g, 0, 0, 0, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 90.00000, 0.5e-5); + result += checkEquals(azi2, 90.00000, 0.5e-5); + result += checkEquals(s12, 20106193, 0.5); + geod_inverse(&g, 0, 0, 0.5, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 33.02493, 0.5e-5); + result += checkEquals(azi2, 146.97364, 0.5e-5); + result += checkEquals(s12, 20082617, 0.5); + geod_inverse(&g, 0, 0, 1, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 20027270, 0.5); + + return result; +} + +static int GeodSolve55() { + /* Check fix for nan + point on equator or pole not returning all nans in + * Geodesic::Inverse, found 2015-09-23. */ + double azi1, azi2, s12, nan; + struct geod_geodesic g; + int result = 0; + { + double minus1 = -1; + /* cppcheck-suppress wrongmathcall */ + nan = sqrt(minus1); + } + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, nan, 0, 0, 90, &s12, &azi1, &azi2); + result += checkNaN(azi1); + result += checkNaN(azi2); + result += checkNaN(s12); + geod_inverse(&g, nan, 0, 90, 9, &s12, &azi1, &azi2); + result += checkNaN(azi1); + result += checkNaN(azi2); + result += checkNaN(s12); + return result; +} + +static int GeodSolve59() { + /* Check for points close with longitudes close to 180 deg apart. */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 5, 0.00000000000001, 10, 180, &s12, &azi1, &azi2); + result += checkEquals(azi1, 0.000000000000035, 1.5e-14); + result += checkEquals(azi2, 179.99999999999996, 1.5e-14); + result += checkEquals(s12, 18345191.174332713, 5e-9); + return result; +} + +static int GeodSolve61() { + /* Make sure small negative azimuths are west-going */ + double lat2, lon2, azi2; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + unsigned flags = GEOD_LONG_UNROLL; + geod_init(&g, wgs84_a, wgs84_f); + geod_gendirect(&g, 45, 0, -0.000000000000000003, flags, 1e7, + &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, 45.30632, 0.5e-5); + result += checkEquals(lon2, -180, 0.5e-5); + result += checkEquals(fabs(azi2), 180, 0.5e-5); + geod_inverseline(&l, &g, 45, 0, 80, -0.000000000000000003, 0); + geod_genposition(&l, flags, 1e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, 45.30632, 0.5e-5); + result += checkEquals(lon2, -180, 0.5e-5); + result += checkEquals(fabs(azi2), 180, 0.5e-5); + return result; +} + +static int GeodSolve65() { + /* Check for bug in east-going check in GeodesicLine (needed to check for + * sign of 0) and sign error in area calculation due to a bogus override of + * the code for alp12. Found/fixed on 2015-12-19. */ + double lat2, lon2, azi2, s12, a12, m12, M12, M21, S12; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + unsigned flags = GEOD_LONG_UNROLL, caps = GEOD_ALL; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverseline(&l, &g, 30, -0.000000000000000001, -31, 180, caps); + a12 = geod_genposition(&l, flags, 1e7, + &lat2, &lon2, &azi2, &s12, &m12, &M12, &M21, &S12); + result += checkEquals(lat2, -60.23169, 0.5e-5); + result += checkEquals(lon2, -0.00000, 0.5e-5); + result += checkEquals(fabs(azi2), 180.00000, 0.5e-5); + result += checkEquals(s12, 10000000, 0.5); + result += checkEquals(a12, 90.06544, 0.5e-5); + result += checkEquals(m12, 6363636, 0.5); + result += checkEquals(M12, -0.0012834, 0.5e-7); + result += checkEquals(M21, 0.0013749, 0.5e-7); + result += checkEquals(S12, 0, 0.5); + a12 = geod_genposition(&l, flags, 2e7, + &lat2, &lon2, &azi2, &s12, &m12, &M12, &M21, &S12); + result += checkEquals(lat2, -30.03547, 0.5e-5); + result += checkEquals(lon2, -180.00000, 0.5e-5); + result += checkEquals(azi2, -0.00000, 0.5e-5); + result += checkEquals(s12, 20000000, 0.5); + result += checkEquals(a12, 179.96459, 0.5e-5); + result += checkEquals(m12, 54342, 0.5); + result += checkEquals(M12, -1.0045592, 0.5e-7); + result += checkEquals(M21, -0.9954339, 0.5e-7); + result += checkEquals(S12, 127516405431022.0, 0.5); + return result; +} + +static int GeodSolve67() { + /* Check for InverseLine if line is slightly west of S and that s13 is + correctly set. */ + double lat2, lon2, azi2; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + unsigned flags = GEOD_LONG_UNROLL; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverseline(&l, &g, -5, -0.000000000000002, -10, 180, 0); + geod_genposition(&l, flags, 2e7, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, 4.96445, 0.5e-5); + result += checkEquals(lon2, -180.00000, 0.5e-5); + result += checkEquals(azi2, -0.00000, 0.5e-5); + geod_genposition(&l, flags, 0.5 * l.s13, &lat2, &lon2, &azi2, 0, 0, 0, 0, 0); + result += checkEquals(lat2, -87.52461, 0.5e-5); + result += checkEquals(lon2, -0.00000, 0.5e-5); + result += checkEquals(azi2, -180.00000, 0.5e-5); + return result; +} + +static int GeodSolve71() { + /* Check that DirectLine sets s13. */ + double lat2, lon2, azi2; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_directline(&l, &g, 1, 2, 45, 1e7, 0); + geod_position(&l, 0.5 * l.s13, &lat2, &lon2, &azi2); + result += checkEquals(lat2, 30.92625, 0.5e-5); + result += checkEquals(lon2, 37.54640, 0.5e-5); + result += checkEquals(azi2, 55.43104, 0.5e-5); + return result; +} + +static int GeodSolve73() { + /* Check for backwards from the pole bug reported by Anon on 2016-02-13. + * This only affected the Java implementation. It was introduced in Java + * version 1.44 and fixed in 1.46-SNAPSHOT on 2016-01-17. */ + double lat2, lon2, azi2; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_direct(&g, 90, 10, 180, -1e6, + &lat2, &lon2, &azi2); + result += checkEquals(lat2, 81.04623, 0.5e-5); + result += checkEquals(lon2, -170, 0.5e-5); + result += azi2 == 0 ? 0 : 1; + result += 1/azi2 > 0 ? 0 : 1; /* Check that azi2 = +0.0 not -0.0 */ + return result; +} + +static void planimeter(const struct geod_geodesic* g, + double points[][2], int N, + double* perimeter, double* area) { + struct geod_polygon p; + int i; + geod_polygon_init(&p, 0); + for (i = 0; i < N; ++i) + geod_polygon_addpoint(g, &p, points[i][0], points[i][1]); + geod_polygon_compute(g, &p, 0, 1, area, perimeter); +} + +static void polylength(const struct geod_geodesic* g, + double points[][2], int N, + double* perimeter) { + struct geod_polygon p; + int i; + geod_polygon_init(&p, 1); + for (i = 0; i < N; ++i) + geod_polygon_addpoint(g, &p, points[i][0], points[i][1]); + geod_polygon_compute(g, &p, 0, 1, 0, perimeter); +} + +static int GeodSolve74() { + /* Check fix for inaccurate areas, bug introduced in v1.46, fixed + 2015-10-16. */ + double a12, s12, azi1, azi2, m12, M12, M21, S12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + a12 = geod_geninverse(&g, 54.1589, 15.3872, 54.1591, 15.3877, + &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); + result += checkEquals(azi1, 55.723110355, 5e-9); + result += checkEquals(azi2, 55.723515675, 5e-9); + result += checkEquals(s12, 39.527686385, 5e-9); + result += checkEquals(a12, 0.000355495, 5e-9); + result += checkEquals(m12, 39.527686385, 5e-9); + result += checkEquals(M12, 0.999999995, 5e-9); + result += checkEquals(M21, 0.999999995, 5e-9); + result += checkEquals(S12, 286698586.30197, 5e-4); + return result; +} + +static int GeodSolve76() { + /* The distance from Wellington and Salamanca (a classic failure of + Vincenty) */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, -(41+19/60.0), 174+49/60.0, 40+58/60.0, -(5+30/60.0), + &s12, &azi1, &azi2); + result += checkEquals(azi1, 160.39137649664, 0.5e-11); + result += checkEquals(azi2, 19.50042925176, 0.5e-11); + result += checkEquals(s12, 19960543.857179, 0.5e-6); + return result; +} + +static int GeodSolve78() { + /* An example where the NGS calculator fails to converge */ + double azi1, azi2, s12; + struct geod_geodesic g; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_inverse(&g, 27.2, 0.0, -27.1, 179.5, &s12, &azi1, &azi2); + result += checkEquals(azi1, 45.82468716758, 0.5e-11); + result += checkEquals(azi2, 134.22776532670, 0.5e-11); + result += checkEquals(s12, 19974354.765767, 0.5e-6); + return result; +} + +static int GeodSolve80() { + /* Some tests to add code coverage: computing scale in special cases + zero + length geodesic (includes GeodSolve80 - GeodSolve83) + using an incapable + line. */ + double a12, s12, azi1, azi2, m12, M12, M21, S12; + struct geod_geodesic g; + struct geod_geodesicline l; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_geninverse(&g, 0, 0, 0, 90, 0, 0, 0, 0, &M12, &M21, 0); + result += checkEquals(M12, -0.00528427534, 0.5e-10); + result += checkEquals(M21, -0.00528427534, 0.5e-10); + geod_geninverse(&g, 0, 0, 1e-6, 1e-6, 0, 0, 0, 0, &M12, &M21, 0); + result += checkEquals(M12, 1, 0.5e-10); + result += checkEquals(M21, 1, 0.5e-10); + a12 = geod_geninverse(&g, 20.001, 0, 20.001, 0, + &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); + result += checkEquals(a12, 0, 1e-13); + result += checkEquals(s12, 0, 1e-8); + result += checkEquals(azi1, 180, 1e-13); + result += checkEquals(azi2, 180, 1e-13); + result += checkEquals(m12, 0, 1e-8); + result += checkEquals(M12, 1, 1e-15); + result += checkEquals(M21, 1, 1e-15); + result += checkEquals(S12, 0, 1e-10); + a12 = geod_geninverse(&g, 90, 0, 90, 180, + &s12, &azi1, &azi2, &m12, &M12, &M21, &S12); + result += checkEquals(a12, 0, 1e-13); + result += checkEquals(s12, 0, 1e-8); + result += checkEquals(azi1, 0, 1e-13); + result += checkEquals(azi2, 180, 1e-13); + result += checkEquals(m12, 0, 1e-8); + result += checkEquals(M12, 1, 1e-15); + result += checkEquals(M21, 1, 1e-15); + result += checkEquals(S12, 127516405431022, 0.5); + /* An incapable line which can't take distance as input */ + geod_lineinit(&l, &g, 1, 2, 90, GEOD_LATITUDE); + a12 = geod_genposition(&l, 0, 1000, 0, 0, 0, 0, 0, 0, 0, 0); + result += checkNaN(a12); + return result; +} + +static int Planimeter0() { + /* Check fix for pole-encircling bug found 2011-03-16 */ + double pa[4][2] = {{89, 0}, {89, 90}, {89, 180}, {89, 270}}; + double pb[4][2] = {{-89, 0}, {-89, 90}, {-89, 180}, {-89, 270}}; + double pc[4][2] = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; + double pd[3][2] = {{90, 0}, {0, 0}, {0, 90}}; + struct geod_geodesic g; + double perimeter, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + + planimeter(&g, pa, 4, &perimeter, &area); + result += checkEquals(perimeter, 631819.8745, 1e-4); + result += checkEquals(area, 24952305678.0, 1); + + planimeter(&g, pb, 4, &perimeter, &area); + result += checkEquals(perimeter, 631819.8745, 1e-4); + result += checkEquals(area, -24952305678.0, 1); + + planimeter(&g, pc, 4, &perimeter, &area); + result += checkEquals(perimeter, 627598.2731, 1e-4); + result += checkEquals(area, 24619419146.0, 1); + + planimeter(&g, pd, 3, &perimeter, &area); + result += checkEquals(perimeter, 30022685, 1); + result += checkEquals(area, 63758202715511.0, 1); + + polylength(&g, pd, 3, &perimeter); + result += checkEquals(perimeter, 20020719, 1); + + return result; +} + +static int Planimeter5() { + /* Check fix for Planimeter pole crossing bug found 2011-06-24 */ + double points[3][2] = {{89, 0.1}, {89, 90.1}, {89, -179.9}}; + struct geod_geodesic g; + double perimeter, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + planimeter(&g, points, 3, &perimeter, &area); + result += checkEquals(perimeter, 539297, 1); + result += checkEquals(area, 12476152838.5, 1); + return result; +} + +static int Planimeter6() { + /* Check fix for Planimeter lon12 rounding bug found 2012-12-03 */ + double pa[3][2] = {{9, -0.00000000000001}, {9, 180}, {9, 0}}; + double pb[3][2] = {{9, 0.00000000000001}, {9, 0}, {9, 180}}; + double pc[3][2] = {{9, 0.00000000000001}, {9, 180}, {9, 0}}; + double pd[3][2] = {{9, -0.00000000000001}, {9, 0}, {9, 180}}; + struct geod_geodesic g; + double perimeter, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + + planimeter(&g, pa, 3, &perimeter, &area); + result += checkEquals(perimeter, 36026861, 1); + result += checkEquals(area, 0, 1); + planimeter(&g, pb, 3, &perimeter, &area); + result += checkEquals(perimeter, 36026861, 1); + result += checkEquals(area, 0, 1); + planimeter(&g, pc, 3, &perimeter, &area); + result += checkEquals(perimeter, 36026861, 1); + result += checkEquals(area, 0, 1); + planimeter(&g, pd, 3, &perimeter, &area); + result += checkEquals(perimeter, 36026861, 1); + result += checkEquals(area, 0, 1); + return result; +} + +static int Planimeter12() { + /* Area of arctic circle (not really -- adjunct to rhumb-area test) */ + double points[2][2] = {{66.562222222, 0}, {66.562222222, 180}}; + struct geod_geodesic g; + double perimeter, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + planimeter(&g, points, 2, &perimeter, &area); + result += checkEquals(perimeter, 10465729, 1); + result += checkEquals(area, 0, 1); + return result; +} + +static int Planimeter13() { + /* Check encircling pole twice */ + double points[6][2] = {{89,-360}, {89,-240}, {89,-120}, + {89,0}, {89,120}, {89,240}}; + struct geod_geodesic g; + double perimeter, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + planimeter(&g, points, 6, &perimeter, &area); + result += checkEquals(perimeter, 1160741, 1); + result += checkEquals(area, 32415230256.0, 1); + return result; +} + +static int Planimeter15() { + /* Coverage tests, includes Planimeter15 - Planimeter18 (combinations of + reverse and sign) + calls to testpoint, testedge, geod_polygonarea. */ + struct geod_geodesic g; + struct geod_polygon p; + double lat[] = {2, 1, 3}, lon[] = {1, 2, 3}; + double area, s12, azi1; + double r = 18454562325.45119, + a0 = 510065621724088.5093; /* ellipsoid area */ + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_polygon_init(&p, 0); + geod_polygon_addpoint(&g, &p, lat[0], lon[0]); + geod_polygon_addpoint(&g, &p, lat[1], lon[1]); + geod_polygon_testpoint(&g, &p, lat[2], lon[2], 0, 1, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_testpoint(&g, &p, lat[2], lon[2], 0, 0, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_testpoint(&g, &p, lat[2], lon[2], 1, 1, &area, 0); + result += checkEquals(area, -r, 0.5); + geod_polygon_testpoint(&g, &p, lat[2], lon[2], 1, 0, &area, 0); + result += checkEquals(area, a0-r, 0.5); + geod_inverse(&g, lat[1], lon[1], lat[2], lon[2], &s12, &azi1, 0); + geod_polygon_testedge(&g, &p, azi1, s12, 0, 1, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_testedge(&g, &p, azi1, s12, 0, 0, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_testedge(&g, &p, azi1, s12, 1, 1, &area, 0); + result += checkEquals(area, -r, 0.5); + geod_polygon_testedge(&g, &p, azi1, s12, 1, 0, &area, 0); + result += checkEquals(area, a0-r, 0.5); + geod_polygon_addpoint(&g, &p, lat[2], lon[2]); + geod_polygon_compute(&g, &p, 0, 1, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_compute(&g, &p, 0, 0, &area, 0); + result += checkEquals(area, r, 0.5); + geod_polygon_compute(&g, &p, 1, 1, &area, 0); + result += checkEquals(area, -r, 0.5); + geod_polygon_compute(&g, &p, 1, 0, &area, 0); + result += checkEquals(area, a0-r, 0.5); + geod_polygonarea(&g, lat, lon, 3, &area, 0); + result += checkEquals(area, r, 0.5); + return result; +} + +static int Planimeter19() { + /* Coverage tests, includes Planimeter19 - Planimeter20 (degenerate + polygons) + extra cases. */ + struct geod_geodesic g; + struct geod_polygon p; + double area, perim; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_polygon_init(&p, 0); + geod_polygon_compute(&g, &p, 0, 1, &area, &perim); + result += area == 0 ? 0 : 1; + result += perim == 0 ? 0 : 1; + geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, &area, &perim); + result += area == 0 ? 0 : 1; + result += perim == 0 ? 0 : 1; + geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, &area, &perim); + result += checkNaN(area); + result += checkNaN(perim); + geod_polygon_addpoint(&g, &p, 1, 1); + geod_polygon_compute(&g, &p, 0, 1, &area, &perim); + result += area == 0 ? 0 : 1; + result += perim == 0 ? 0 : 1; + geod_polygon_init(&p, 1); + geod_polygon_compute(&g, &p, 0, 1, 0, &perim); + result += perim == 0 ? 0 : 1; + geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, 0, &perim); + result += perim == 0 ? 0 : 1; + geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); + result += checkNaN(perim); + geod_polygon_addpoint(&g, &p, 1, 1); + geod_polygon_compute(&g, &p, 0, 1, 0, &perim); + result += perim == 0 ? 0 : 1; + return result; +} + +static int Planimeter21() { + /* Some test to add code coverage: multiple circlings of pole (includes + Planimeter21 - Planimeter28) + invocations via testpoint and testedge. + Some of the results for i = 4 in the loop are wrong because we don't + reduce the area to the allowed range correctly. However these cases are + not "simple" polygons, so we'll defer fixing the problem for now. + */ + struct geod_geodesic g; + struct geod_polygon p; + double area, lat = 45, + a = 39.2144607176828184218, s = 8420705.40957178156285, + r = 39433884866571.4277, /* Area for one circuit */ + a0 = 510065621724088.5093; /* Ellipsoid area */ + int result = 0, i; + geod_init(&g, wgs84_a, wgs84_f); + geod_polygon_init(&p, 0); + geod_polygon_addpoint(&g, &p, lat, 60); + geod_polygon_addpoint(&g, &p, lat, 180); + geod_polygon_addpoint(&g, &p, lat, -60); + geod_polygon_addpoint(&g, &p, lat, 60); + geod_polygon_addpoint(&g, &p, lat, 180); + geod_polygon_addpoint(&g, &p, lat, -60); + for (i = 3; i <= 4; ++i) { + geod_polygon_addpoint(&g, &p, lat, 60); + geod_polygon_addpoint(&g, &p, lat, 180); + geod_polygon_testpoint(&g, &p, lat, -60, 0, 1, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_testpoint(&g, &p, lat, -60, 0, 0, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_testpoint(&g, &p, lat, -60, 1, 1, &area, 0); + if (i != 4) result += checkEquals(area, -i*r, 0.5); + geod_polygon_testpoint(&g, &p, lat, -60, 1, 0, &area, 0); + result += checkEquals(area, -i*r + a0, 0.5); + geod_polygon_testedge(&g, &p, a, s, 0, 1, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_testedge(&g, &p, a, s, 0, 0, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_testedge(&g, &p, a, s, 1, 1, &area, 0); + if (i != 4) result += checkEquals(area, -i*r, 0.5); + geod_polygon_testedge(&g, &p, a, s, 1, 0, &area, 0); + result += checkEquals(area, -i*r + a0, 0.5); + geod_polygon_addpoint(&g, &p, lat, -60); + geod_polygon_compute(&g, &p, 0, 1, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_compute(&g, &p, 0, 0, &area, 0); + if (i != 4) result += checkEquals(area, i*r, 0.5); + geod_polygon_compute(&g, &p, 1, 1, &area, 0); + if (i != 4) result += checkEquals(area, -i*r, 0.5); + geod_polygon_compute(&g, &p, 1, 0, &area, 0); + result += checkEquals(area, -i*r + a0, 0.5); + } + return result; +} + +static int AddEdge1() { + /* Check fix to transitdirect vs transit zero handling inconsistency */ + struct geod_geodesic g; + struct geod_polygon p; + double area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_polygon_init(&p, 0); + geod_polygon_addpoint(&g, &p, 0, 0); + geod_polygon_addedge(&g, &p, 90, 1000); + geod_polygon_addedge(&g, &p, 0, 1000); + geod_polygon_addedge(&g, &p, -90, 1000); + geod_polygon_compute(&g, &p, 0, 1, &area, 0); + result += checkEquals(area, 1000000.0, 0.01); + return result; +} + +static int EmptyPoly() { + struct geod_geodesic g; + struct geod_polygon p; + double perim, area; + int result = 0; + geod_init(&g, wgs84_a, wgs84_f); + geod_polygon_init(&p, 0); + geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, &area, &perim); + result += area == 0 ? 0 : 1; + result += perim == 0 ? 0 : 1; + geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, &area, &perim); + result += checkNaN(area); + result += checkNaN(perim); + geod_polygon_compute(&g, &p, 0, 1, &area, &perim); + result += area == 0 ? 0 : 1; + result += perim == 0 ? 0 : 1; + geod_polygon_init(&p, 1); + geod_polygon_testpoint(&g, &p, 1, 1, 0, 1, 0, &perim); + result += perim == 0 ? 0 : 1; + geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); + result += checkNaN(perim); + geod_polygon_compute(&g, &p, 0, 1, 0, &perim); + result += perim == 0 ? 0 : 1; + geod_polygon_addpoint(&g, &p, 1, 1); + geod_polygon_testedge(&g, &p, 90, 1000, 0, 1, 0, &perim); + result += checkEquals(perim, 1000, 1e-10); + geod_polygon_testpoint(&g, &p, 2, 2, 0, 1, 0, &perim); + result += checkEquals(perim, 156876.149, 0.5e-3); + return result; +} + +int main() { + int n = 0, i; + if ((i = testinverse())) {++n; printf("testinverse fail: %d\n", i);} + if ((i = testdirect())) {++n; printf("testdirect fail: %d\n", i);} + if ((i = testarcdirect())) {++n; printf("testarcdirect fail: %d\n", i);} + if ((i = GeodSolve0())) {++n; printf("GeodSolve0 fail: %d\n", i);} + if ((i = GeodSolve1())) {++n; printf("GeodSolve1 fail: %d\n", i);} + if ((i = GeodSolve2())) {++n; printf("GeodSolve2 fail: %d\n", i);} + if ((i = GeodSolve4())) {++n; printf("GeodSolve4 fail: %d\n", i);} + if ((i = GeodSolve5())) {++n; printf("GeodSolve5 fail: %d\n", i);} + if ((i = GeodSolve6())) {++n; printf("GeodSolve6 fail: %d\n", i);} + if ((i = GeodSolve9())) {++n; printf("GeodSolve9 fail: %d\n", i);} + if ((i = GeodSolve10())) {++n; printf("GeodSolve10 fail: %d\n", i);} + if ((i = GeodSolve11())) {++n; printf("GeodSolve11 fail: %d\n", i);} + if ((i = GeodSolve12())) {++n; printf("GeodSolve12 fail: %d\n", i);} + if ((i = GeodSolve14())) {++n; printf("GeodSolve14 fail: %d\n", i);} + if ((i = GeodSolve15())) {++n; printf("GeodSolve15 fail: %d\n", i);} + if ((i = GeodSolve17())) {++n; printf("GeodSolve17 fail: %d\n", i);} + if ((i = GeodSolve26())) {++n; printf("GeodSolve26 fail: %d\n", i);} + if ((i = GeodSolve28())) {++n; printf("GeodSolve28 fail: %d\n", i);} + if ((i = GeodSolve33())) {++n; printf("GeodSolve33 fail: %d\n", i);} + if ((i = GeodSolve55())) {++n; printf("GeodSolve55 fail: %d\n", i);} + if ((i = GeodSolve59())) {++n; printf("GeodSolve59 fail: %d\n", i);} + if ((i = GeodSolve61())) {++n; printf("GeodSolve61 fail: %d\n", i);} + if ((i = GeodSolve65())) {++n; printf("GeodSolve65 fail: %d\n", i);} + if ((i = GeodSolve67())) {++n; printf("GeodSolve67 fail: %d\n", i);} + if ((i = GeodSolve71())) {++n; printf("GeodSolve71 fail: %d\n", i);} + if ((i = GeodSolve73())) {++n; printf("GeodSolve73 fail: %d\n", i);} + if ((i = GeodSolve74())) {++n; printf("GeodSolve74 fail: %d\n", i);} + if ((i = GeodSolve76())) {++n; printf("GeodSolve76 fail: %d\n", i);} + if ((i = GeodSolve78())) {++n; printf("GeodSolve78 fail: %d\n", i);} + if ((i = GeodSolve80())) {++n; printf("GeodSolve80 fail: %d\n", i);} + if ((i = Planimeter0())) {++n; printf("Planimeter0 fail: %d\n", i);} + if ((i = Planimeter5())) {++n; printf("Planimeter5 fail: %d\n", i);} + if ((i = Planimeter6())) {++n; printf("Planimeter6 fail: %d\n", i);} + if ((i = Planimeter12())) {++n; printf("Planimeter12 fail: %d\n", i);} + if ((i = Planimeter13())) {++n; printf("Planimeter13 fail: %d\n", i);} + if ((i = Planimeter15())) {++n; printf("Planimeter15 fail: %d\n", i);} + if ((i = Planimeter19())) {++n; printf("Planimeter19 fail: %d\n", i);} + if ((i = Planimeter21())) {++n; printf("Planimeter21 fail: %d\n", i);} + if ((i = AddEdge1())) {++n; printf("AddEdge1 fail: %d\n", i);} + if ((i = EmptyPoly())) {++n; printf("EmptyPoly fail: %d\n", i);} + return n; +} + +/** @endcond */ diff --git a/src/gie.c b/src/gie.c deleted file mode 100644 index 4bb79f1f..00000000 --- a/src/gie.c +++ /dev/null @@ -1,1456 +0,0 @@ -/*********************************************************************** - - gie - The Geospatial Integrity Investigation Environment - -************************************************************************ - -The Geospatial Integrity Investigation Environment "gie" is a modest -regression testing environment for the PROJ.4 transformation library. - -Its primary design goal was to be able to replace those thousands of -lines of regression testing code that are (at time of writing) part -of PROJ.4, while not requiring any other kind of tooling than the same -C compiler already employed for compiling the library. - -The basic functionality of the gie command language is implemented -through just 3 command verbs: - -operation, which defines the PROJ.4 operation to test, -accept, which defines the input coordinate to read, and -expect, which defines the result to expect. - -E.g: - -operation +proj=utm +zone=32 +ellps=GRS80 -accept 12 55 -expect 691_875.632_14 6_098_907.825_05 - -Note that gie accepts the underscore ("_") as a thousands separator. -It is not required (in fact, it is entirely ignored by the input -routine), but it significantly improves the readability of the very -long strings of numbers typically required in projected coordinates. - -By default, gie considers the EXPECTation met, if the result comes to -within 0.5 mm of the expected. This default can be changed using the -'tolerance' command verb (and yes, I know, linguistically speaking, both -"operation" and "tolerance" are nouns, not verbs). See the first -few hundred lines of the "builtins.gie" test file for more details of -the command verbs available (verbs of both the VERBal and NOUNistic -persuation). - --- - -But more importantly than being an acronym for "Geospatial Integrity -Investigation Environment", gie were also the initials, user id, and -USGS email address of Gerald Ian Evenden (1935--2016), the geospatial -visionary, who, already in the 1980s, started what was to become the -PROJ.4 of today. - -Gerald's clear vision was that map projections are *just special -functions*. Some of them rather complex, most of them of two variables, -but all of them *just special functions*, and not particularly more -special than the sin(), cos(), tan(), and hypot() already available in -the C standard library. - -And hence, according to Gerald, *they should not be particularly much -harder to use*, for a programmer, than the sin()s, tan()s and hypot()s -so readily available. - -Gerald's ingenuity also showed in the implementation of the vision, -where he devised a comprehensive, yet simple, system of key-value -pairs for parameterising a map projection, and the highly flexible -PJ struct, storing run-time compiled versions of those key-value pairs, -hence making a map projection function call, pj_fwd(PJ, point), as easy -as a traditional function call like hypot(x,y). - -While today, we may have more formally well defined metadata systems -(most prominent the OGC WKT2 representation), nothing comes close being -as easily readable ("human compatible") as Gerald's key-value system. -This system in particular, and the PROJ.4 system in general, was -Gerald's great gift to anyone using and/or communicating about geodata. - -It is only reasonable to name a program, keeping an eye on the integrity -of the PROJ.4 system, in honour of Gerald. - -So in honour, and hopefully also in the spirit, of Gerald Ian Evenden -(1935--2016), this is the Geospatial Integrity Investigation Environment. - -************************************************************************ - -Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08 - -************************************************************************ - -* Copyright (c) 2017 Thomas Knudsen -* Copyright (c) 2017, SDFE -* -* 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 -#include -#include -#include -#include -#include -#include -#include - -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "proj_strtod.h" -#include "projects.h" - -#include "optargpm.h" - -/* Package for flexible format I/O - ffio */ -typedef struct ffio { - FILE *f; - const char **tags; - const char *tag; - char *args; - char *next_args; - size_t n_tags; - size_t args_size; - size_t next_args_size; - size_t argc; - size_t lineno, next_lineno; - size_t level; -} ffio; - -static int get_inp (ffio *G); -static int skip_to_next_tag (ffio *G); -static int step_into_gie_block (ffio *G); -static int locate_tag (ffio *G, const char *tag); -static int nextline (ffio *G); -static int at_end_delimiter (ffio *G); -static const char *at_tag (ffio *G); -static int at_decorative_element (ffio *G); -static ffio *ffio_destroy (ffio *G); -static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size); - -static const char *gie_tags[] = { - "", "operation", "use_proj4_init_rules", - "accept", "expect", "roundtrip", "banner", "verbose", - "direction", "tolerance", "ignore", "require_grid", "echo", "skip", "" -}; - -static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; - - -int main(int argc, char **argv); - -static int dispatch (const char *cmnd, const char *args); -static int errmsg (int errlev, const char *msg, ...); -static int errno_from_err_const (const char *err_const); -static int list_err_codes (void); -static int process_file (const char *fname); - -static const char *column (const char *buf, int n); -static const char *err_const_from_errno (int err); - - - -#define SKIP -1 - -#define MAX_OPERATION 10000 - -typedef struct { - char operation[MAX_OPERATION+1]; - PJ *P; - PJ_COORD a, b, c, e; - PJ_DIRECTION dir; - int verbosity; - int skip; - int op_id; - int op_ok, op_ko, op_skip; - int total_ok, total_ko, total_skip; - int grand_ok, grand_ko, grand_skip; - size_t operation_lineno; - size_t dimensions_given, dimensions_given_at_last_accept; - double tolerance; - int use_proj4_init_rules; - int ignore; - int skip_test; - const char *curr_file; - FILE *fout; -} gie_ctx; - -ffio *F = 0; - -static gie_ctx T; -int tests=0, succs=0, succ_fails=0, fail_fails=0, succ_rtps=0, fail_rtps=0; - -static const char delim[] = {"-------------------------------------------------------------------------------\n"}; - - -static const char usage[] = { - "--------------------------------------------------------------------------------\n" - "Usage: %s [-options]... infile...\n" - "--------------------------------------------------------------------------------\n" - "Options:\n" - "--------------------------------------------------------------------------------\n" - " -h Help: print this usage information\n" - " -o /path/to/file Specify output file name\n" - " -v Verbose: Provide non-essential informational output.\n" - " Repeat -v for more verbosity (e.g. -vv)\n" - " -q Quiet: Opposite of verbose. In quiet mode not even errors\n" - " are reported. Only interaction is through the return code\n" - " (0 on success, non-zero indicates number of FAILED tests)\n" - " -l List the PROJ internal system error codes\n" - "--------------------------------------------------------------------------------\n" - "Long Options:\n" - "--------------------------------------------------------------------------------\n" - " --output Alias for -o\n" - " --verbose Alias for -v\n" - " --help Alias for -h\n" - " --list Alias for -l\n" - " --version Print version number\n" - "--------------------------------------------------------------------------------\n" - "Examples:\n" - "--------------------------------------------------------------------------------\n" - "1. Run all tests in file \"corner-cases.gie\", providing much extra information\n" - " gie -vvvv corner-cases.gie\n" - "2. Run all tests in files \"foo\" and \"bar\", providing info on failures only\n" - " gie foo bar\n" - "--------------------------------------------------------------------------------\n" -}; - -int main (int argc, char **argv) { - int i; - const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", "version", 0}; - const char *longkeys[] = {"o=output", 0}; - OPTARGS *o; - - memset (&T, 0, sizeof (T)); - T.dir = PJ_FWD; - T.verbosity = 1; - T.tolerance = 5e-4; - T.ignore = 5555; /* Error code that will not be issued by proj_create() */ - T.use_proj4_init_rules = FALSE; - - o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); - if (0==o) - return 0; - - if (opt_given (o, "h") || argc==1) { - printf (usage, o->progname); - free (o); - return 0; - } - - - if (opt_given (o, "version")) { - fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ()); - free (o); - return 0; - } - - T.verbosity = opt_given (o, "q"); - if (T.verbosity) - T.verbosity = -1; - if (T.verbosity != -1) - T.verbosity = opt_given (o, "v") + 1; - - T.fout = stdout; - if (opt_given (o, "o")) - T.fout = fopen (opt_arg (o, "output"), "rt"); - - if (0==T.fout) { - fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); - free (o); - return 1; - } - - if (opt_given (o, "l")) { - free (o); - return list_err_codes (); - } - - if (0==o->fargc) { - if (T.verbosity==-1) - return -1; - fprintf (T.fout, "Nothing to do\n"); - free (o); - return 0; - } - - F = ffio_create (gie_tags, n_gie_tags, 1000); - if (0==F) { - fprintf (stderr, "%s: No memory\n", o->progname); - free (o); - return 1; - } - - for (i = 0; i < o->fargc; i++) - process_file (o->fargv[i]); - - if (T.verbosity > 0) { - if (o->fargc > 1) { - fprintf (T.fout, "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n", - delim, T.grand_ok+T.grand_ko+T.grand_skip, T.grand_ok, T.grand_skip, - T.grand_ko); - } - fprintf (T.fout, "%s", delim); - if (T.verbosity > 1) { - fprintf (T.fout, "Failing roundtrips: %4d, Succeeding roundtrips: %4d\n", fail_rtps, succ_rtps); - fprintf (T.fout, "Failing failures: %4d, Succeeding failures: %4d\n", fail_fails, succ_fails); - fprintf (T.fout, "Internal counters: %4.4d(%4.4d)\n", tests, succs); - fprintf (T.fout, "%s", delim); - } - } - else - if (T.grand_ko) - fprintf (T.fout, "Failures: %d", T.grand_ko); - - if (stdout != T.fout) - fclose (T.fout); - - free (o); - ffio_destroy (F); - return T.grand_ko; -} - -static int another_failure (void) { - T.op_ko++; - T.total_ko++; - proj_errno_reset (T.P); - return 0; -} - -static int another_skip (void) { - T.op_skip++; - T.total_skip++; - return 0; -} - -static int another_success (void) { - T.op_ok++; - T.total_ok++; - proj_errno_reset (T.P); - return 0; -} - -static int another_succeeding_failure (void) { - succ_fails++; - return another_success (); -} - -static int another_failing_failure (void) { - fail_fails++; - return another_failure (); -} - -static int another_succeeding_roundtrip (void) { - succ_rtps++; - return another_success (); -} - -static int another_failing_roundtrip (void) { - fail_rtps++; - return another_failure (); -} - -static int process_file (const char *fname) { - FILE *f; - - F->lineno = F->next_lineno = F->level = 0; - T.op_ok = T.total_ok = 0; - T.op_ko = T.total_ko = 0; - T.op_skip = T.total_skip = 0; - - if (T.skip) { - proj_destroy (T.P); - T.P = 0; - return 0; - } - - f = fopen (fname, "rt"); - if (0==f) { - if (T.verbosity > 0) { - fprintf (T.fout, "%sCannot open spec'd input file '%s' - bye!\n", delim, fname); - return 2; - } - errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname); - } - F->f = f; - - if (T.verbosity > 0) - fprintf (T.fout, "%sReading file '%s'\n", delim, fname); - T.curr_file = fname; - - while (get_inp(F)) { - if (SKIP==dispatch (F->tag, F->args)) { - proj_destroy (T.P); - T.P = 0; - return 0; - } - } - - fclose (f); - F->lineno = F->next_lineno = 0; - - T.grand_ok += T.total_ok; - T.grand_ko += T.total_ko; - T.grand_skip += T.grand_skip; - if (T.verbosity > 0) { - fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n", - delim, T.total_ok, T.total_skip, T.total_ko, - T.total_ko? "FAILED!": "failed."); - } - if (F->level==0) - return errmsg (-3, "File '%s':Missing '' cmnd - bye!\n", fname); - if (F->level && F->level%2) - return errmsg (-4, "File '%s':Missing '' cmnd - bye!\n", fname); - return 0; -} - - -/*****************************************************************************/ -const char *column (const char *buf, int n) { -/***************************************************************************** -Return a pointer to the n'th column of buf. Column numbers start at 0. -******************************************************************************/ - int i; - if (n <= 0) - return buf; - for (i = 0; i < n; i++) { - while (isspace(*buf)) - buf++; - if (i == n - 1) - break; - while ((0 != *buf) && !isspace(*buf)) - buf++; - } - return buf; -} - - -/*****************************************************************************/ -static double strtod_scaled (const char *args, double default_scale) { -/***************************************************************************** -Interpret as a numeric followed by a linear decadal prefix. -Return the properly scaled numeric -******************************************************************************/ - double s; - const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */ - const char *endp = args; - s = proj_strtod (args, (char **) &endp); - if (args==endp) - return HUGE_VAL; - - endp = column (args, 2); - - if (0==strcmp(endp, "km")) - s *= 1000; - else if (0==strcmp(endp, "m")) - s *= 1; - else if (0==strcmp(endp, "dm")) - s /= 10; - else if (0==strcmp(endp, "cm")) - s /= 100; - else if (0==strcmp(endp, "mm")) - s /= 1000; - else if (0==strcmp(endp, "um")) - s /= 1e6; - else if (0==strcmp(endp, "nm")) - s /= 1e9; - else if (0==strcmp(endp, "rad")) - s = GRS80_DEG * proj_todeg (s); - else if (0==strcmp(endp, "deg")) - s = GRS80_DEG * s; - else - s *= default_scale; - return s; -} - - -static int banner (const char *args) { - char dots[] = {"..."}, nodots[] = {""}, *thedots = nodots; - if (strlen(args) > 70) - thedots = dots; - fprintf (T.fout, "%s%-70.70s%s\n", delim, args, thedots); - return 0; -} - - -static int tolerance (const char *args) { - T.tolerance = strtod_scaled (args, 1); - if (HUGE_VAL==T.tolerance) { - T.tolerance = 0.0005; - return 1; - } - return 0; -} - - -static int use_proj4_init_rules (const char *args) { - T.use_proj4_init_rules = strcmp(args, "true") == 0; - return 0; -} - -static int ignore (const char *args) { - T.ignore = errno_from_err_const (column (args, 1)); - return 0; -} - -static int require_grid (const char *args) { - PJ_GRID_INFO grid_info; - const char* grid_filename = column (args, 1); - grid_info = proj_grid_info(grid_filename); - if( strlen(grid_info.filename) == 0 ) { - if (T.verbosity > 1) { - fprintf (T.fout, "Test skipped because of missing grid %s\n", - grid_filename); - } - T.skip_test = 1; - } - return 0; -} - -static int direction (const char *args) { - const char *endp = args; - while (isspace (*endp)) - endp++; - switch (*endp) { - case 'F': - case 'f': - T.dir = PJ_FWD; - break; - case 'I': - case 'i': - case 'R': - case 'r': - T.dir = PJ_INV; - break; - default: - return 1; - } - - return 0; -} - - -static void finish_previous_operation (const char *args) { - if (T.verbosity > 1 && T.op_id > 1 && T.op_ok+T.op_ko) - fprintf (T.fout, "%s %d tests succeeded, %d tests skipped, %d tests %s\n", - delim, T.op_ok, T.op_skip, T.op_ko, T.op_ko? "FAILED!": "failed."); - (void) args; -} - - - -/*****************************************************************************/ -static int operation (char *args) { -/***************************************************************************** -Define the operation to apply to the input data (in ISO 19100 lingo, -an operation is the general term describing something that can be -either a conversion or a transformation) -******************************************************************************/ - T.op_id++; - - T.operation_lineno = F->lineno; - - strncpy (&(T.operation[0]), F->args, MAX_OPERATION); - T.operation[MAX_OPERATION] = '\0'; - - if (T.verbosity > 1) { - finish_previous_operation (F->args); - banner (args); - } - - - T.op_ok = 0; - T.op_ko = 0; - T.op_skip = 0; - T.skip_test = 0; - - direction ("forward"); - tolerance ("0.5 mm"); - ignore ("pjd_err_dont_skip"); - - proj_errno_reset (T.P); - - if (T.P) - proj_destroy (T.P); - proj_errno_reset (0); - proj_context_use_proj4_init_rules(0, T.use_proj4_init_rules); - - T.P = proj_create (0, F->args); - - /* Checking that proj_create succeeds is first done at "expect" time, */ - /* since we want to support "expect"ing specific error codes */ - - return 0; -} - -static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { - size_t i, n; - char *axis = "enut"; - paralist *l = pj_param_exists (P->params, "axis"); - if (l && dir==PJ_INV) - axis = l->param + strlen ("axis="); - n = strlen (axis); - for (i = 0; i < n; i++) - if (strchr ("news", axis[i])) - a.v[i] = proj_torad (a.v[i]); - return a; -} - - -static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { - size_t i, n; - char *axis = "enut"; - paralist *l = pj_param_exists (P->params, "axis"); - if (l && dir==PJ_FWD) - axis = l->param + strlen ("axis="); - n = strlen (axis); - for (i = 0; i < n; i++) - if (strchr ("news", axis[i])) - a.v[i] = proj_todeg (a.v[i]); - return a; -} - - - -/*****************************************************************************/ -static PJ_COORD parse_coord (const char *args) { -/***************************************************************************** -Attempt to interpret args as a PJ_COORD. -******************************************************************************/ - int i; - const char *endp; - const char *dmsendp; - const char *prev = args; - PJ_COORD a = proj_coord (0,0,0,0); - - T.dimensions_given = 0; - for (i = 0; i < 4; i++) { - /* proj_strtod doesn't read values like 123d45'678W so we need a bit */ - /* of help from proj_dmstor. proj_strtod effectively ignores what */ - /* comes after "d", so we use that fact that when dms is larger than */ - /* d the value was stated in "dms" form. */ - /* This could be avoided if proj_dmstor used the same proj_strtod() */ - /* as gie, but that is not the case (yet). When we remove projects.h */ - /* from the public API we can change that. */ - double d = proj_strtod(prev, (char **) &endp); - double dms = PJ_TODEG(proj_dmstor (prev, (char **) &dmsendp)); - /* TODO: When projects.h is removed, call proj_dmstor() in all cases */ - if (d != dms && fabs(d) < fabs(dms) && fabs(dms) < fabs(d) + 1) { - d = dms; - endp = dmsendp; - } - /* A number like -81d00'00.000 will be parsed correctly by both */ - /* proj_strtod and proj_dmstor but only the latter will return */ - /* the correct end-pointer. */ - if (d == dms && endp != dmsendp) - endp = dmsendp; - - /* Break out if there were no more numerals */ - if (prev==endp) - return i > 1? a: proj_coord_error (); - - a.v[i] = d; - prev = endp; - T.dimensions_given++; - } - - return a; -} - - -/*****************************************************************************/ -static int accept (const char *args) { -/***************************************************************************** -Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. -******************************************************************************/ - T.a = parse_coord (args); - if (T.verbosity > 3) - fprintf (T.fout, "# %s\n", args); - T.dimensions_given_at_last_accept = T.dimensions_given; - return 0; -} - - -/*****************************************************************************/ -static int roundtrip (const char *args) { -/***************************************************************************** -Check how far we go from the ACCEPTed point when doing successive -back/forward transformation pairs. - -Without args, roundtrip defaults to 100 iterations: - - roundtrip - -With one arg, roundtrip will default to a tolerance of T.tolerance: - - roundtrip ntrips - -With two args: - - roundtrip ntrips tolerance - -Always returns 0. -******************************************************************************/ - int ntrips; - double d, r, ans; - char *endp; - PJ_COORD coo; - - if (0==T.P) { - if (T.ignore == proj_errno(T.P)) - return another_skip(); - - return another_failure (); - } - - ans = proj_strtod (args, &endp); - if (endp==args) { - /* Default to 100 iterations if not args. */ - ntrips = 100; - } else { - if (ans < 1.0 || ans > 1000000.0) { - errmsg (2, "Invalid number of roundtrips: %lf\n", ans); - return another_failing_roundtrip (); - } - ntrips = (int)ans; - } - - d = strtod_scaled (endp, 1); - d = d==HUGE_VAL? T.tolerance: d; - - /* input ("accepted") values - probably in degrees */ - coo = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; - - r = proj_roundtrip (T.P, T.dir, ntrips, &coo); - if (r <= d) - return another_succeeding_roundtrip (); - - if (T.verbosity > -1) { - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d); - } - return another_failing_roundtrip (); -} - - -static int expect_message (double d, const char *args) { - another_failure (); - - if (T.verbosity < 0) - return 1; - if (d > 1e6) - d = 999999.999999; - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " expected: %s\n", args); - fprintf (T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y); - if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0) - fprintf (T.fout, " %.9f", T.b.xyz.z); - if (T.b.xyzt.t!=0) - fprintf (T.fout, " %.9f", T.b.xyzt.t); - fprintf (T.fout, "\n"); - fprintf (T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000*d, 1000*T.tolerance); - return 1; -} - - -static int expect_message_cannot_parse (const char *args) { - another_failure (); - if (T.verbosity > -1) { - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args); - } - return 1; -} - -static int expect_failure_with_errno_message (int expected, int got) { - another_failing_failure (); - - if (T.verbosity < 0) - return 1; - if (0==T.op_ko && T.verbosity < 2) - banner (T.operation); - fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); - fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); - fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, pj_strerrno (got)); - fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, pj_strerrno (expected)); - fprintf (T.fout, "\n"); - return 1; -} - - -/* For test purposes, we want to call a transformation of the same */ -/* dimensionality as the number of dimensions given in accept */ -static PJ_COORD expect_trans_n_dim (PJ_COORD ci) { - if (4==T.dimensions_given_at_last_accept) - return proj_trans (T.P, T.dir, ci); - - if (3==T.dimensions_given_at_last_accept) - return pj_approx_3D_trans (T.P, T.dir, ci); - - return pj_approx_2D_trans (T.P, T.dir, ci); -} - - -/*****************************************************************************/ -static int expect (const char *args) { -/***************************************************************************** -Tell GIE what to expect, when transforming the ACCEPTed input -******************************************************************************/ - PJ_COORD ci, co, ce; - double d; - int expect_failure = 0; - int expect_failure_with_errno = 0; - - if (0==strncmp (args, "failure", 7)) { - expect_failure = 1; - - /* Option: Fail with an expected errno (syntax: expect failure errno -33) */ - if (0==strncmp (column (args, 2), "errno", 5)) - expect_failure_with_errno = errno_from_err_const (column (args, 3)); - } - - if (T.ignore==proj_errno(T.P)) - return another_skip (); - - if (0==T.P) { - /* If we expect failure, and fail, then it's a success... */ - if (expect_failure) { - /* Failed to fail correctly? */ - if (expect_failure_with_errno && proj_errno (T.P)!=expect_failure_with_errno) - return expect_failure_with_errno_message (expect_failure_with_errno, proj_errno(T.P)); - - return another_succeeding_failure (); - } - - /* Otherwise, it's a true failure */ - banner (T.operation); - errmsg (3, "%sInvalid operation definition in line no. %d:\n %s (errno=%s/%d)\n", - delim, (int) T.operation_lineno, pj_strerrno(proj_errno(T.P)), - err_const_from_errno (proj_errno(T.P)), proj_errno(T.P) - ); - return another_failing_failure (); - } - - /* We may still successfully fail even if the proj_create succeeded */ - if (expect_failure) { - proj_errno_reset (T.P); - - /* Try to carry out the operation - and expect failure */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; - co = expect_trans_n_dim (ci); - - if (expect_failure_with_errno) { - if (proj_errno (T.P)==expect_failure_with_errno) - return another_succeeding_failure (); - fprintf (T.fout, "errno=%d, expected=%d\n", proj_errno (T.P), expect_failure_with_errno); - return another_failing_failure (); - } - - - /* Succeeded in failing? - that's a success */ - if (co.xyz.x==HUGE_VAL) - return another_succeeding_failure (); - - /* Failed to fail? - that's a failure */ - banner (T.operation); - errmsg (3, "%sFailed to fail. Operation definition in line no. %d\n", - delim, (int) T.operation_lineno - ); - return another_failing_failure (); - } - - - if (T.verbosity > 3) { - fprintf (T.fout, "%s\n", T.P->inverted? "INVERTED": "NOT INVERTED"); - fprintf (T.fout, "%s\n", T.dir== 1? "forward": "reverse"); - fprintf (T.fout, "%s\n", proj_angular_input (T.P, T.dir)? "angular in": "linear in"); - fprintf (T.fout, "%s\n", proj_angular_output (T.P, T.dir)? "angular out": "linear out"); - fprintf (T.fout, "left: %d right: %d\n", T.P->left, T.P->right); - } - - tests++; - T.e = parse_coord (args); - if (HUGE_VAL==T.e.v[0]) - return expect_message_cannot_parse (args); - - - /* expected angular values, probably in degrees */ - ce = proj_angular_output (T.P, T.dir)? torad_coord (T.P, T.dir, T.e): T.e; - if (T.verbosity > 3) - fprintf (T.fout, "EXPECTS %.12f %.12f %.12f %.12f\n", - ce.v[0],ce.v[1],ce.v[2],ce.v[3]); - - /* input ("accepted") values, also probably in degrees */ - ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; - if (T.verbosity > 3) - fprintf (T.fout, "ACCEPTS %.12f %.12f %.12f %.12f\n", - ci.v[0],ci.v[1],ci.v[2],ci.v[3]); - - /* do the transformation, but mask off dimensions not given in expect-ation */ - co = expect_trans_n_dim (ci); - if (T.dimensions_given < 4) - co.v[3] = 0; - if (T.dimensions_given < 3) - co.v[2] = 0; - - /* angular output from proj_trans comes in radians */ - T.b = proj_angular_output (T.P, T.dir)? todeg_coord (T.P, T.dir, co): co; - if (T.verbosity > 3) - fprintf (T.fout, "GOT %.12f %.12f %.12f %.12f\n", - co.v[0],co.v[1],co.v[2],co.v[3]); - -#if 0 - /* We need to handle unusual axis orders - that'll be an item for version 5.1 */ - if (T.P->axisswap) { - ce = proj_trans (T.P->axisswap, T.dir, ce); - co = proj_trans (T.P->axisswap, T.dir, co); - } -#endif - if (proj_angular_output (T.P, T.dir)) - d = proj_lpz_dist (T.P, ce, co); - else - d = proj_xyz_dist (co, ce); - - if (d > T.tolerance) - return expect_message (d, args); - succs++; - - another_success (); - return 0; -} - - - -/*****************************************************************************/ -static int verbose (const char *args) { -/***************************************************************************** -Tell the system how noisy it should be -******************************************************************************/ - int i = (int) proj_atof (args); - - /* if -q/--quiet flag has been given, we do nothing */ - if (T.verbosity < 0) - return 0; - - if (strlen (args)) - T.verbosity = i; - else - T.verbosity++; - return 0; -} - - -/*****************************************************************************/ -static int echo (const char *args) { -/***************************************************************************** -Add user defined noise to the output stream -******************************************************************************/ -fprintf (T.fout, "%s\n", args); - return 0; -} - - - -/*****************************************************************************/ -static int skip (const char *args) { -/***************************************************************************** -Indicate that the remaining material should be skipped. Mostly for debugging. -******************************************************************************/ - T.skip = 1; - (void) args; - F->level = 2; /* Silence complaints about missing element */ - return 0; -} - - -static int dispatch (const char *cmnd, const char *args) { - if (T.skip) - return SKIP; - if (0==strcmp (cmnd, "operation")) return operation ((char *) args); - if (T.skip_test) - { - if (0==strcmp (cmnd, "expect")) return another_skip(); - return 0; - } - if (0==strcmp (cmnd, "accept")) return accept (args); - if (0==strcmp (cmnd, "expect")) return expect (args); - if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args); - if (0==strcmp (cmnd, "banner")) return banner (args); - if (0==strcmp (cmnd, "verbose")) return verbose (args); - if (0==strcmp (cmnd, "direction")) return direction (args); - if (0==strcmp (cmnd, "tolerance")) return tolerance (args); - if (0==strcmp (cmnd, "ignore")) return ignore (args); - if (0==strcmp (cmnd, "require_grid")) return require_grid (args); - if (0==strcmp (cmnd, "echo")) return echo (args); - if (0==strcmp (cmnd, "skip")) return skip (args); - if (0==strcmp (cmnd, "use_proj4_init_rules")) - return use_proj4_init_rules (args); - - return 0; -} - - - - -struct errno_vs_err_const {const char *the_err_const; int the_errno;}; -static const struct errno_vs_err_const lookup[] = { - {"pjd_err_no_args" , -1}, - {"pjd_err_no_option_in_init_file" , -2}, - {"pjd_err_no_colon_in_init_string" , -3}, - {"pjd_err_proj_not_named" , -4}, - {"pjd_err_unknown_projection_id" , -5}, - {"pjd_err_eccentricity_is_one" , -6}, - {"pjd_err_unknown_unit_id" , -7}, - {"pjd_err_invalid_boolean_param" , -8}, - {"pjd_err_unknown_ellp_param" , -9}, - {"pjd_err_rev_flattening_is_zero" , -10}, - {"pjd_err_ref_rad_larger_than_90" , -11}, - {"pjd_err_es_less_than_zero" , -12}, - {"pjd_err_major_axis_not_given" , -13}, - {"pjd_err_lat_or_lon_exceed_limit" , -14}, - {"pjd_err_invalid_x_or_y" , -15}, - {"pjd_err_wrong_format_dms_value" , -16}, - {"pjd_err_non_conv_inv_meri_dist" , -17}, - {"pjd_err_non_con_inv_phi2" , -18}, - {"pjd_err_acos_asin_arg_too_large" , -19}, - {"pjd_err_tolerance_condition" , -20}, - {"pjd_err_conic_lat_equal" , -21}, - {"pjd_err_lat_larger_than_90" , -22}, - {"pjd_err_lat1_is_zero" , -23}, - {"pjd_err_lat_ts_larger_than_90" , -24}, - {"pjd_err_control_point_no_dist" , -25}, - {"pjd_err_no_rotation_proj" , -26}, - {"pjd_err_w_or_m_zero_or_less" , -27}, - {"pjd_err_lsat_not_in_range" , -28}, - {"pjd_err_path_not_in_range" , -29}, - {"pjd_err_h_less_than_zero" , -30}, - {"pjd_err_k_less_than_zero" , -31}, - {"pjd_err_lat_1_or_2_zero_or_90" , -32}, - {"pjd_err_lat_0_or_alpha_eq_90" , -33}, - {"pjd_err_ellipsoid_use_required" , -34}, - {"pjd_err_invalid_utm_zone" , -35}, - {"pjd_err_tcheby_val_out_of_range" , -36}, - {"pjd_err_failed_to_find_proj" , -37}, - {"pjd_err_failed_to_load_grid" , -38}, - {"pjd_err_invalid_m_or_n" , -39}, - {"pjd_err_n_out_of_range" , -40}, - {"pjd_err_lat_1_2_unspecified" , -41}, - {"pjd_err_abs_lat1_eq_abs_lat2" , -42}, - {"pjd_err_lat_0_half_pi_from_mean" , -43}, - {"pjd_err_unparseable_cs_def" , -44}, - {"pjd_err_geocentric" , -45}, - {"pjd_err_unknown_prime_meridian" , -46}, - {"pjd_err_axis" , -47}, - {"pjd_err_grid_area" , -48}, - {"pjd_err_invalid_sweep_axis" , -49}, - {"pjd_err_malformed_pipeline" , -50}, - {"pjd_err_unit_factor_less_than_0" , -51}, - {"pjd_err_invalid_scale" , -52}, - {"pjd_err_non_convergent" , -53}, - {"pjd_err_missing_args" , -54}, - {"pjd_err_lat_0_is_zero" , -55}, - {"pjd_err_ellipsoidal_unsupported" , -56}, - {"pjd_err_too_many_inits" , -57}, - {"pjd_err_invalid_arg" , -58}, - {"pjd_err_dont_skip" , 5555}, - {"pjd_err_unknown" , 9999}, - {"pjd_err_enomem" , ENOMEM}, -}; - -static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999}; - - -static int list_err_codes (void) { - int i; - const int n = sizeof lookup / sizeof lookup[0]; - - for (i = 0; i < n; i++) { - if (9999==lookup[i].the_errno) - break; - fprintf (T.fout, "%25s (%2.2d): %s\n", lookup[i].the_err_const + 8, - lookup[i].the_errno, pj_strerrno(lookup[i].the_errno)); - } - return 0; -} - - -static const char *err_const_from_errno (int err) { - size_t i; - const size_t n = sizeof lookup / sizeof lookup[0]; - - for (i = 0; i < n; i++) { - if (err==lookup[i].the_errno) - return lookup[i].the_err_const + 8; - } - return unknown.the_err_const; -} - - -static int errno_from_err_const (const char *err_const) { - const size_t n = sizeof lookup / sizeof lookup[0]; - size_t i, len; - int ret; - char tolower_err_const[100]; - - /* Make a lower case copy for matching */ - for (i = 0; i < 99; i++) { - if (0==err_const[i] || isspace (err_const[i])) - break; - tolower_err_const[i] = (char) tolower (err_const[i]); - } - tolower_err_const[i] = 0; - - /* If it looks numeric, return that numeric */ - ret = (int) pj_atof (err_const); - if (0!=ret) - return ret; - - /* Else try to find a matching identifier */ - len = strlen (tolower_err_const); - - /* First try to find a match excluding the PJD_ERR_ prefix */ - for (i = 0; i < n; i++) { - if (0==strncmp (lookup[i].the_err_const + 8, err_const, len)) - return lookup[i].the_errno; - } - - /* If that did not work, try with the full name */ - for (i = 0; i < n; i++) { - if (0==strncmp (lookup[i].the_err_const, err_const, len)) - return lookup[i].the_errno; - } - - /* On failure, return something unlikely */ - return 9999; -} - - -static int errmsg (int errlev, const char *msg, ...) { - va_list args; - va_start(args, msg); - vfprintf(stdout, msg, args); - va_end(args); - if (errlev) - errno = errlev; - return errlev; -} - - - - - - - - -/**************************************************************************************** - -FFIO - Flexible format I/O - -FFIO provides functionality for reading proj style instruction strings written -in a less strict format than usual: - -* Whitespace is generally allowed everywhere -* Comments can be written inline, '#' style -* ... or as free format blocks - -The overall mission of FFIO is to facilitate communications of geodetic -parameters and test material in a format that is highly human readable, -and provides ample room for comment, documentation, and test material. - -See the PROJ ".gie" test suites for examples of supported formatting. - -****************************************************************************************/ - - -/***************************************************************************************/ -static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) { -/**************************************************************************************** -Constructor for the ffio object. -****************************************************************************************/ - ffio *G = calloc (1, sizeof (ffio)); - if (0==G) - return 0; - - if (0==max_record_size) - max_record_size = 1000; - - G->args = calloc (1, 5*max_record_size); - if (0==G->args) { - free (G); - return 0; - } - - G->next_args = calloc (1, max_record_size); - if (0==G->args) { - free (G->args); - free (G); - return 0; - } - - G->args_size = 5*max_record_size; - G->next_args_size = max_record_size; - - G->tags = tags; - G->n_tags = n_tags; - return G; -} - - - -/***************************************************************************************/ -static ffio *ffio_destroy (ffio *G) { -/**************************************************************************************** -Free all allocated associated memory, then free G itself. For extra RAII compliancy, -the file object should also be closed if still open, but this will require additional -control logic, and ffio is a gie tool specific package, so we fall back to asserting that -fclose has been called prior to ffio_destroy. -****************************************************************************************/ - free (G->args); - free (G->next_args); - free (G); - return 0; -} - - - -/***************************************************************************************/ -static int at_decorative_element (ffio *G) { -/**************************************************************************************** -A decorative element consists of a line of at least 5 consecutive identical chars, -starting at buffer position 0: -"-----", "=====", "*****", etc. - -A decorative element serves as a end delimiter for the current element, and -continues until a gie command verb is found at the start of a line -****************************************************************************************/ - int i; - char *c; - if (0==G) - return 0; - c = G->next_args; - if (0==c) - return 0; - if (0==c[0]) - return 0; - for (i = 1; i < 5; i++) - if (c[i]!=c[0]) - return 0; - return 1; -} - - - -/***************************************************************************************/ -static const char *at_tag (ffio *G) { -/**************************************************************************************** -A start of a new command serves as an end delimiter for the current command -****************************************************************************************/ - size_t j; - for (j = 0; j < G->n_tags; j++) - if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0) - return G->tags[j]; - return 0; -} - - - -/***************************************************************************************/ -static int at_end_delimiter (ffio *G) { -/**************************************************************************************** -An instruction consists of everything from its introductory tag to its end -delimiter. An end delimiter can be either the introductory tag of the next -instruction, or a "decorative element", i.e. one of the "ascii art" style -block delimiters typically used to mark up block comments in a free format -file. -****************************************************************************************/ - if (G==0) - return 0; - if (at_decorative_element (G)) - return 1; - if (at_tag (G)) - return 1; - return 0; -} - - - -/***************************************************************************************/ -static int nextline (ffio *G) { -/**************************************************************************************** -Read next line of input file. Returns 1 on success, 0 on failure. -****************************************************************************************/ - G->next_args[0] = 0; - if (T.skip) - return 0; - if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) - return 0; - if (feof (G->f)) - return 0; - pj_chomp (G->next_args); - G->next_lineno++; - return 1; -} - - - -/***************************************************************************************/ -static int locate_tag (ffio *G, const char *tag) { -/**************************************************************************************** -Find start-of-line tag (currently only used to search for for , but any tag -valid). - -Returns 1 on success, 0 on failure. -****************************************************************************************/ - size_t n = strlen (tag); - while (0!=strncmp (tag, G->next_args, n)) - if (0==nextline (G)) - return 0; - return 1; -} - - - -/***************************************************************************************/ -static int step_into_gie_block (ffio *G) { -/**************************************************************************************** -Make sure we're inside a -block. Return 1 on success, 0 otherwise. -****************************************************************************************/ - /* Already inside */ - if (G->level % 2) - return 1; - - if (0==locate_tag (G, "")) - return 0; - - while (0!=strncmp ("", G->next_args, 5)) { - G->next_args[0] = 0; - if (feof (G->f)) - return 0; - if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) - return 0; - pj_chomp (G->next_args); - G->next_lineno++; - } - G->level++; - - /* We're ready at the start - now step into the block */ - return nextline (G); -} - - - -/***************************************************************************************/ -static int skip_to_next_tag (ffio *G) { -/**************************************************************************************** -Skip forward to the next command tag. Return 1 on success, 0 otherwise. -****************************************************************************************/ - const char *c; - if (0==step_into_gie_block (G)) - return 0; - - c = at_tag (G); - - /* If not already there - get there */ - while (!c) { - if (0==nextline (G)) - return 0; - c = at_tag (G); - } - - /* If we reached the end of a block, locate the next and retry */ - if (0==strcmp (c, "")) { - G->level++; - if (feof (G->f)) - return 0; - if (0==step_into_gie_block (G)) - return 0; - G->args[0] = 0; - return skip_to_next_tag (G); - } - G->lineno = G->next_lineno; - - return 1; -} - -/* Add the most recently read line of input to the block already stored. */ -static int append_args (ffio *G) { - size_t skip_chars = 0; - size_t next_len = strlen (G->next_args); - size_t args_len = strlen (G->args); - const char *tag = at_tag (G); - - if (tag) - skip_chars = strlen (tag); - - /* +2: 1 for the space separator and 1 for the NUL termination. */ - if (G->args_size < args_len + next_len - skip_chars + 2) { - void *p = realloc (G->args, 2 * G->args_size); - if (0==p) - return 0; - G->args = p; - G->args_size = 2 * G->args_size; - } - - G->args[args_len] = ' '; - strcpy (G->args + args_len + 1, G->next_args + skip_chars); - - G->next_args[0] = 0; - return 1; -} - - - - - -/***************************************************************************************/ -static int get_inp (ffio *G) { -/**************************************************************************************** -The primary command reader for gie. Reads a block of gie input, cleans up repeated -whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise. -****************************************************************************************/ - G->args[0] = 0; - - if (0==skip_to_next_tag (G)) - return 0; - G->tag = at_tag (G); - - if (0==G->tag) - return 0; - - do { - append_args (G); - if (0==nextline (G)) - return 0; - } while (!at_end_delimiter (G)); - - pj_shrink (G->args); - return 1; -} diff --git a/src/gie.cpp b/src/gie.cpp new file mode 100644 index 00000000..74d78db5 --- /dev/null +++ b/src/gie.cpp @@ -0,0 +1,1456 @@ +/*********************************************************************** + + gie - The Geospatial Integrity Investigation Environment + +************************************************************************ + +The Geospatial Integrity Investigation Environment "gie" is a modest +regression testing environment for the PROJ.4 transformation library. + +Its primary design goal was to be able to replace those thousands of +lines of regression testing code that are (at time of writing) part +of PROJ.4, while not requiring any other kind of tooling than the same +C compiler already employed for compiling the library. + +The basic functionality of the gie command language is implemented +through just 3 command verbs: + +operation, which defines the PROJ.4 operation to test, +accept, which defines the input coordinate to read, and +expect, which defines the result to expect. + +E.g: + +operation +proj=utm +zone=32 +ellps=GRS80 +accept 12 55 +expect 691_875.632_14 6_098_907.825_05 + +Note that gie accepts the underscore ("_") as a thousands separator. +It is not required (in fact, it is entirely ignored by the input +routine), but it significantly improves the readability of the very +long strings of numbers typically required in projected coordinates. + +By default, gie considers the EXPECTation met, if the result comes to +within 0.5 mm of the expected. This default can be changed using the +'tolerance' command verb (and yes, I know, linguistically speaking, both +"operation" and "tolerance" are nouns, not verbs). See the first +few hundred lines of the "builtins.gie" test file for more details of +the command verbs available (verbs of both the VERBal and NOUNistic +persuation). + +-- + +But more importantly than being an acronym for "Geospatial Integrity +Investigation Environment", gie were also the initials, user id, and +USGS email address of Gerald Ian Evenden (1935--2016), the geospatial +visionary, who, already in the 1980s, started what was to become the +PROJ.4 of today. + +Gerald's clear vision was that map projections are *just special +functions*. Some of them rather complex, most of them of two variables, +but all of them *just special functions*, and not particularly more +special than the sin(), cos(), tan(), and hypot() already available in +the C standard library. + +And hence, according to Gerald, *they should not be particularly much +harder to use*, for a programmer, than the sin()s, tan()s and hypot()s +so readily available. + +Gerald's ingenuity also showed in the implementation of the vision, +where he devised a comprehensive, yet simple, system of key-value +pairs for parameterising a map projection, and the highly flexible +PJ struct, storing run-time compiled versions of those key-value pairs, +hence making a map projection function call, pj_fwd(PJ, point), as easy +as a traditional function call like hypot(x,y). + +While today, we may have more formally well defined metadata systems +(most prominent the OGC WKT2 representation), nothing comes close being +as easily readable ("human compatible") as Gerald's key-value system. +This system in particular, and the PROJ.4 system in general, was +Gerald's great gift to anyone using and/or communicating about geodata. + +It is only reasonable to name a program, keeping an eye on the integrity +of the PROJ.4 system, in honour of Gerald. + +So in honour, and hopefully also in the spirit, of Gerald Ian Evenden +(1935--2016), this is the Geospatial Integrity Investigation Environment. + +************************************************************************ + +Thomas Knudsen, thokn@sdfe.dk, 2017-10-01/2017-10-08 + +************************************************************************ + +* Copyright (c) 2017 Thomas Knudsen +* Copyright (c) 2017, SDFE +* +* 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 +#include +#include +#include +#include +#include +#include +#include + +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "proj_strtod.h" +#include "projects.h" + +#include "optargpm.h" + +/* Package for flexible format I/O - ffio */ +typedef struct ffio { + FILE *f; + const char **tags; + const char *tag; + char *args; + char *next_args; + size_t n_tags; + size_t args_size; + size_t next_args_size; + size_t argc; + size_t lineno, next_lineno; + size_t level; +} ffio; + +static int get_inp (ffio *G); +static int skip_to_next_tag (ffio *G); +static int step_into_gie_block (ffio *G); +static int locate_tag (ffio *G, const char *tag); +static int nextline (ffio *G); +static int at_end_delimiter (ffio *G); +static const char *at_tag (ffio *G); +static int at_decorative_element (ffio *G); +static ffio *ffio_destroy (ffio *G); +static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size); + +static const char *gie_tags[] = { + "", "operation", "use_proj4_init_rules", + "accept", "expect", "roundtrip", "banner", "verbose", + "direction", "tolerance", "ignore", "require_grid", "echo", "skip", "" +}; + +static const size_t n_gie_tags = sizeof gie_tags / sizeof gie_tags[0]; + + +int main(int argc, char **argv); + +static int dispatch (const char *cmnd, const char *args); +static int errmsg (int errlev, const char *msg, ...); +static int errno_from_err_const (const char *err_const); +static int list_err_codes (void); +static int process_file (const char *fname); + +static const char *column (const char *buf, int n); +static const char *err_const_from_errno (int err); + + + +#define SKIP -1 + +#define MAX_OPERATION 10000 + +typedef struct { + char operation[MAX_OPERATION+1]; + PJ *P; + PJ_COORD a, b, c, e; + PJ_DIRECTION dir; + int verbosity; + int skip; + int op_id; + int op_ok, op_ko, op_skip; + int total_ok, total_ko, total_skip; + int grand_ok, grand_ko, grand_skip; + size_t operation_lineno; + size_t dimensions_given, dimensions_given_at_last_accept; + double tolerance; + int use_proj4_init_rules; + int ignore; + int skip_test; + const char *curr_file; + FILE *fout; +} gie_ctx; + +ffio *F = 0; + +static gie_ctx T; +int tests=0, succs=0, succ_fails=0, fail_fails=0, succ_rtps=0, fail_rtps=0; + +static const char delim[] = {"-------------------------------------------------------------------------------\n"}; + + +static const char usage[] = { + "--------------------------------------------------------------------------------\n" + "Usage: %s [-options]... infile...\n" + "--------------------------------------------------------------------------------\n" + "Options:\n" + "--------------------------------------------------------------------------------\n" + " -h Help: print this usage information\n" + " -o /path/to/file Specify output file name\n" + " -v Verbose: Provide non-essential informational output.\n" + " Repeat -v for more verbosity (e.g. -vv)\n" + " -q Quiet: Opposite of verbose. In quiet mode not even errors\n" + " are reported. Only interaction is through the return code\n" + " (0 on success, non-zero indicates number of FAILED tests)\n" + " -l List the PROJ internal system error codes\n" + "--------------------------------------------------------------------------------\n" + "Long Options:\n" + "--------------------------------------------------------------------------------\n" + " --output Alias for -o\n" + " --verbose Alias for -v\n" + " --help Alias for -h\n" + " --list Alias for -l\n" + " --version Print version number\n" + "--------------------------------------------------------------------------------\n" + "Examples:\n" + "--------------------------------------------------------------------------------\n" + "1. Run all tests in file \"corner-cases.gie\", providing much extra information\n" + " gie -vvvv corner-cases.gie\n" + "2. Run all tests in files \"foo\" and \"bar\", providing info on failures only\n" + " gie foo bar\n" + "--------------------------------------------------------------------------------\n" +}; + +int main (int argc, char **argv) { + int i; + const char *longflags[] = {"v=verbose", "q=quiet", "h=help", "l=list", "version", 0}; + const char *longkeys[] = {"o=output", 0}; + OPTARGS *o; + + memset (&T, 0, sizeof (T)); + T.dir = PJ_FWD; + T.verbosity = 1; + T.tolerance = 5e-4; + T.ignore = 5555; /* Error code that will not be issued by proj_create() */ + T.use_proj4_init_rules = FALSE; + + o = opt_parse (argc, argv, "hlvq", "o", longflags, longkeys); + if (0==o) + return 0; + + if (opt_given (o, "h") || argc==1) { + printf (usage, o->progname); + free (o); + return 0; + } + + + if (opt_given (o, "version")) { + fprintf (stdout, "%s: %s\n", o->progname, pj_get_release ()); + free (o); + return 0; + } + + T.verbosity = opt_given (o, "q"); + if (T.verbosity) + T.verbosity = -1; + if (T.verbosity != -1) + T.verbosity = opt_given (o, "v") + 1; + + T.fout = stdout; + if (opt_given (o, "o")) + T.fout = fopen (opt_arg (o, "output"), "rt"); + + if (0==T.fout) { + fprintf (stderr, "%s: Cannot open '%s' for output\n", o->progname, opt_arg (o, "output")); + free (o); + return 1; + } + + if (opt_given (o, "l")) { + free (o); + return list_err_codes (); + } + + if (0==o->fargc) { + if (T.verbosity==-1) + return -1; + fprintf (T.fout, "Nothing to do\n"); + free (o); + return 0; + } + + F = ffio_create (gie_tags, n_gie_tags, 1000); + if (0==F) { + fprintf (stderr, "%s: No memory\n", o->progname); + free (o); + return 1; + } + + for (i = 0; i < o->fargc; i++) + process_file (o->fargv[i]); + + if (T.verbosity > 0) { + if (o->fargc > 1) { + fprintf (T.fout, "%sGrand total: %d. Success: %d, Skipped: %d, Failure: %d\n", + delim, T.grand_ok+T.grand_ko+T.grand_skip, T.grand_ok, T.grand_skip, + T.grand_ko); + } + fprintf (T.fout, "%s", delim); + if (T.verbosity > 1) { + fprintf (T.fout, "Failing roundtrips: %4d, Succeeding roundtrips: %4d\n", fail_rtps, succ_rtps); + fprintf (T.fout, "Failing failures: %4d, Succeeding failures: %4d\n", fail_fails, succ_fails); + fprintf (T.fout, "Internal counters: %4.4d(%4.4d)\n", tests, succs); + fprintf (T.fout, "%s", delim); + } + } + else + if (T.grand_ko) + fprintf (T.fout, "Failures: %d", T.grand_ko); + + if (stdout != T.fout) + fclose (T.fout); + + free (o); + ffio_destroy (F); + return T.grand_ko; +} + +static int another_failure (void) { + T.op_ko++; + T.total_ko++; + proj_errno_reset (T.P); + return 0; +} + +static int another_skip (void) { + T.op_skip++; + T.total_skip++; + return 0; +} + +static int another_success (void) { + T.op_ok++; + T.total_ok++; + proj_errno_reset (T.P); + return 0; +} + +static int another_succeeding_failure (void) { + succ_fails++; + return another_success (); +} + +static int another_failing_failure (void) { + fail_fails++; + return another_failure (); +} + +static int another_succeeding_roundtrip (void) { + succ_rtps++; + return another_success (); +} + +static int another_failing_roundtrip (void) { + fail_rtps++; + return another_failure (); +} + +static int process_file (const char *fname) { + FILE *f; + + F->lineno = F->next_lineno = F->level = 0; + T.op_ok = T.total_ok = 0; + T.op_ko = T.total_ko = 0; + T.op_skip = T.total_skip = 0; + + if (T.skip) { + proj_destroy (T.P); + T.P = 0; + return 0; + } + + f = fopen (fname, "rt"); + if (0==f) { + if (T.verbosity > 0) { + fprintf (T.fout, "%sCannot open spec'd input file '%s' - bye!\n", delim, fname); + return 2; + } + errmsg (2, "Cannot open spec'd input file '%s' - bye!\n", fname); + } + F->f = f; + + if (T.verbosity > 0) + fprintf (T.fout, "%sReading file '%s'\n", delim, fname); + T.curr_file = fname; + + while (get_inp(F)) { + if (SKIP==dispatch (F->tag, F->args)) { + proj_destroy (T.P); + T.P = 0; + return 0; + } + } + + fclose (f); + F->lineno = F->next_lineno = 0; + + T.grand_ok += T.total_ok; + T.grand_ko += T.total_ko; + T.grand_skip += T.grand_skip; + if (T.verbosity > 0) { + fprintf (T.fout, "%stotal: %2d tests succeeded, %2d tests skipped, %2d tests %s\n", + delim, T.total_ok, T.total_skip, T.total_ko, + T.total_ko? "FAILED!": "failed."); + } + if (F->level==0) + return errmsg (-3, "File '%s':Missing '' cmnd - bye!\n", fname); + if (F->level && F->level%2) + return errmsg (-4, "File '%s':Missing '' cmnd - bye!\n", fname); + return 0; +} + + +/*****************************************************************************/ +const char *column (const char *buf, int n) { +/***************************************************************************** +Return a pointer to the n'th column of buf. Column numbers start at 0. +******************************************************************************/ + int i; + if (n <= 0) + return buf; + for (i = 0; i < n; i++) { + while (isspace(*buf)) + buf++; + if (i == n - 1) + break; + while ((0 != *buf) && !isspace(*buf)) + buf++; + } + return buf; +} + + +/*****************************************************************************/ +static double strtod_scaled (const char *args, double default_scale) { +/***************************************************************************** +Interpret as a numeric followed by a linear decadal prefix. +Return the properly scaled numeric +******************************************************************************/ + double s; + const double GRS80_DEG = 111319.4908; /* deg-to-m at equator of GRS80 */ + const char *endp = args; + s = proj_strtod (args, (char **) &endp); + if (args==endp) + return HUGE_VAL; + + endp = column (args, 2); + + if (0==strcmp(endp, "km")) + s *= 1000; + else if (0==strcmp(endp, "m")) + s *= 1; + else if (0==strcmp(endp, "dm")) + s /= 10; + else if (0==strcmp(endp, "cm")) + s /= 100; + else if (0==strcmp(endp, "mm")) + s /= 1000; + else if (0==strcmp(endp, "um")) + s /= 1e6; + else if (0==strcmp(endp, "nm")) + s /= 1e9; + else if (0==strcmp(endp, "rad")) + s = GRS80_DEG * proj_todeg (s); + else if (0==strcmp(endp, "deg")) + s = GRS80_DEG * s; + else + s *= default_scale; + return s; +} + + +static int banner (const char *args) { + char dots[] = {"..."}, nodots[] = {""}, *thedots = nodots; + if (strlen(args) > 70) + thedots = dots; + fprintf (T.fout, "%s%-70.70s%s\n", delim, args, thedots); + return 0; +} + + +static int tolerance (const char *args) { + T.tolerance = strtod_scaled (args, 1); + if (HUGE_VAL==T.tolerance) { + T.tolerance = 0.0005; + return 1; + } + return 0; +} + + +static int use_proj4_init_rules (const char *args) { + T.use_proj4_init_rules = strcmp(args, "true") == 0; + return 0; +} + +static int ignore (const char *args) { + T.ignore = errno_from_err_const (column (args, 1)); + return 0; +} + +static int require_grid (const char *args) { + PJ_GRID_INFO grid_info; + const char* grid_filename = column (args, 1); + grid_info = proj_grid_info(grid_filename); + if( strlen(grid_info.filename) == 0 ) { + if (T.verbosity > 1) { + fprintf (T.fout, "Test skipped because of missing grid %s\n", + grid_filename); + } + T.skip_test = 1; + } + return 0; +} + +static int direction (const char *args) { + const char *endp = args; + while (isspace (*endp)) + endp++; + switch (*endp) { + case 'F': + case 'f': + T.dir = PJ_FWD; + break; + case 'I': + case 'i': + case 'R': + case 'r': + T.dir = PJ_INV; + break; + default: + return 1; + } + + return 0; +} + + +static void finish_previous_operation (const char *args) { + if (T.verbosity > 1 && T.op_id > 1 && T.op_ok+T.op_ko) + fprintf (T.fout, "%s %d tests succeeded, %d tests skipped, %d tests %s\n", + delim, T.op_ok, T.op_skip, T.op_ko, T.op_ko? "FAILED!": "failed."); + (void) args; +} + + + +/*****************************************************************************/ +static int operation (char *args) { +/***************************************************************************** +Define the operation to apply to the input data (in ISO 19100 lingo, +an operation is the general term describing something that can be +either a conversion or a transformation) +******************************************************************************/ + T.op_id++; + + T.operation_lineno = F->lineno; + + strncpy (&(T.operation[0]), F->args, MAX_OPERATION); + T.operation[MAX_OPERATION] = '\0'; + + if (T.verbosity > 1) { + finish_previous_operation (F->args); + banner (args); + } + + + T.op_ok = 0; + T.op_ko = 0; + T.op_skip = 0; + T.skip_test = 0; + + direction ("forward"); + tolerance ("0.5 mm"); + ignore ("pjd_err_dont_skip"); + + proj_errno_reset (T.P); + + if (T.P) + proj_destroy (T.P); + proj_errno_reset (0); + proj_context_use_proj4_init_rules(0, T.use_proj4_init_rules); + + T.P = proj_create (0, F->args); + + /* Checking that proj_create succeeds is first done at "expect" time, */ + /* since we want to support "expect"ing specific error codes */ + + return 0; +} + +static PJ_COORD torad_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { + size_t i, n; + char *axis = "enut"; + paralist *l = pj_param_exists (P->params, "axis"); + if (l && dir==PJ_INV) + axis = l->param + strlen ("axis="); + n = strlen (axis); + for (i = 0; i < n; i++) + if (strchr ("news", axis[i])) + a.v[i] = proj_torad (a.v[i]); + return a; +} + + +static PJ_COORD todeg_coord (PJ *P, PJ_DIRECTION dir, PJ_COORD a) { + size_t i, n; + char *axis = "enut"; + paralist *l = pj_param_exists (P->params, "axis"); + if (l && dir==PJ_FWD) + axis = l->param + strlen ("axis="); + n = strlen (axis); + for (i = 0; i < n; i++) + if (strchr ("news", axis[i])) + a.v[i] = proj_todeg (a.v[i]); + return a; +} + + + +/*****************************************************************************/ +static PJ_COORD parse_coord (const char *args) { +/***************************************************************************** +Attempt to interpret args as a PJ_COORD. +******************************************************************************/ + int i; + const char *endp; + const char *dmsendp; + const char *prev = args; + PJ_COORD a = proj_coord (0,0,0,0); + + T.dimensions_given = 0; + for (i = 0; i < 4; i++) { + /* proj_strtod doesn't read values like 123d45'678W so we need a bit */ + /* of help from proj_dmstor. proj_strtod effectively ignores what */ + /* comes after "d", so we use that fact that when dms is larger than */ + /* d the value was stated in "dms" form. */ + /* This could be avoided if proj_dmstor used the same proj_strtod() */ + /* as gie, but that is not the case (yet). When we remove projects.h */ + /* from the public API we can change that. */ + double d = proj_strtod(prev, (char **) &endp); + double dms = PJ_TODEG(proj_dmstor (prev, (char **) &dmsendp)); + /* TODO: When projects.h is removed, call proj_dmstor() in all cases */ + if (d != dms && fabs(d) < fabs(dms) && fabs(dms) < fabs(d) + 1) { + d = dms; + endp = dmsendp; + } + /* A number like -81d00'00.000 will be parsed correctly by both */ + /* proj_strtod and proj_dmstor but only the latter will return */ + /* the correct end-pointer. */ + if (d == dms && endp != dmsendp) + endp = dmsendp; + + /* Break out if there were no more numerals */ + if (prev==endp) + return i > 1? a: proj_coord_error (); + + a.v[i] = d; + prev = endp; + T.dimensions_given++; + } + + return a; +} + + +/*****************************************************************************/ +static int accept (const char *args) { +/***************************************************************************** +Read ("ACCEPT") a 2, 3, or 4 dimensional input coordinate. +******************************************************************************/ + T.a = parse_coord (args); + if (T.verbosity > 3) + fprintf (T.fout, "# %s\n", args); + T.dimensions_given_at_last_accept = T.dimensions_given; + return 0; +} + + +/*****************************************************************************/ +static int roundtrip (const char *args) { +/***************************************************************************** +Check how far we go from the ACCEPTed point when doing successive +back/forward transformation pairs. + +Without args, roundtrip defaults to 100 iterations: + + roundtrip + +With one arg, roundtrip will default to a tolerance of T.tolerance: + + roundtrip ntrips + +With two args: + + roundtrip ntrips tolerance + +Always returns 0. +******************************************************************************/ + int ntrips; + double d, r, ans; + char *endp; + PJ_COORD coo; + + if (0==T.P) { + if (T.ignore == proj_errno(T.P)) + return another_skip(); + + return another_failure (); + } + + ans = proj_strtod (args, &endp); + if (endp==args) { + /* Default to 100 iterations if not args. */ + ntrips = 100; + } else { + if (ans < 1.0 || ans > 1000000.0) { + errmsg (2, "Invalid number of roundtrips: %lf\n", ans); + return another_failing_roundtrip (); + } + ntrips = (int)ans; + } + + d = strtod_scaled (endp, 1); + d = d==HUGE_VAL? T.tolerance: d; + + /* input ("accepted") values - probably in degrees */ + coo = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; + + r = proj_roundtrip (T.P, T.dir, ntrips, &coo); + if (r <= d) + return another_succeeding_roundtrip (); + + if (T.verbosity > -1) { + if (0==T.op_ko && T.verbosity < 2) + banner (T.operation); + fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); + fprintf (T.fout, " roundtrip deviation: %.6f mm, expected: %.6f mm\n", 1000*r, 1000*d); + } + return another_failing_roundtrip (); +} + + +static int expect_message (double d, const char *args) { + another_failure (); + + if (T.verbosity < 0) + return 1; + if (d > 1e6) + d = 999999.999999; + if (0==T.op_ko && T.verbosity < 2) + banner (T.operation); + fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); + + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); + fprintf (T.fout, " expected: %s\n", args); + fprintf (T.fout, " got: %.12f %.12f", T.b.xy.x, T.b.xy.y); + if (T.b.xyzt.t!=0 || T.b.xyzt.z!=0) + fprintf (T.fout, " %.9f", T.b.xyz.z); + if (T.b.xyzt.t!=0) + fprintf (T.fout, " %.9f", T.b.xyzt.t); + fprintf (T.fout, "\n"); + fprintf (T.fout, " deviation: %.6f mm, expected: %.6f mm\n", 1000*d, 1000*T.tolerance); + return 1; +} + + +static int expect_message_cannot_parse (const char *args) { + another_failure (); + if (T.verbosity > -1) { + if (0==T.op_ko && T.verbosity < 2) + banner (T.operation); + fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); + fprintf (T.fout, " FAILURE in %s(%d):\n Too few args: %s\n", opt_strip_path (T.curr_file), (int) F->lineno, args); + } + return 1; +} + +static int expect_failure_with_errno_message (int expected, int got) { + another_failing_failure (); + + if (T.verbosity < 0) + return 1; + if (0==T.op_ko && T.verbosity < 2) + banner (T.operation); + fprintf (T.fout, "%s", T.op_ko? " -----\n": delim); + fprintf (T.fout, " FAILURE in %s(%d):\n", opt_strip_path (T.curr_file), (int) F->lineno); + fprintf (T.fout, " got errno %s (%d): %s\n", err_const_from_errno(got), got, pj_strerrno (got)); + fprintf (T.fout, " expected %s (%d): %s", err_const_from_errno(expected), expected, pj_strerrno (expected)); + fprintf (T.fout, "\n"); + return 1; +} + + +/* For test purposes, we want to call a transformation of the same */ +/* dimensionality as the number of dimensions given in accept */ +static PJ_COORD expect_trans_n_dim (PJ_COORD ci) { + if (4==T.dimensions_given_at_last_accept) + return proj_trans (T.P, T.dir, ci); + + if (3==T.dimensions_given_at_last_accept) + return pj_approx_3D_trans (T.P, T.dir, ci); + + return pj_approx_2D_trans (T.P, T.dir, ci); +} + + +/*****************************************************************************/ +static int expect (const char *args) { +/***************************************************************************** +Tell GIE what to expect, when transforming the ACCEPTed input +******************************************************************************/ + PJ_COORD ci, co, ce; + double d; + int expect_failure = 0; + int expect_failure_with_errno = 0; + + if (0==strncmp (args, "failure", 7)) { + expect_failure = 1; + + /* Option: Fail with an expected errno (syntax: expect failure errno -33) */ + if (0==strncmp (column (args, 2), "errno", 5)) + expect_failure_with_errno = errno_from_err_const (column (args, 3)); + } + + if (T.ignore==proj_errno(T.P)) + return another_skip (); + + if (0==T.P) { + /* If we expect failure, and fail, then it's a success... */ + if (expect_failure) { + /* Failed to fail correctly? */ + if (expect_failure_with_errno && proj_errno (T.P)!=expect_failure_with_errno) + return expect_failure_with_errno_message (expect_failure_with_errno, proj_errno(T.P)); + + return another_succeeding_failure (); + } + + /* Otherwise, it's a true failure */ + banner (T.operation); + errmsg (3, "%sInvalid operation definition in line no. %d:\n %s (errno=%s/%d)\n", + delim, (int) T.operation_lineno, pj_strerrno(proj_errno(T.P)), + err_const_from_errno (proj_errno(T.P)), proj_errno(T.P) + ); + return another_failing_failure (); + } + + /* We may still successfully fail even if the proj_create succeeded */ + if (expect_failure) { + proj_errno_reset (T.P); + + /* Try to carry out the operation - and expect failure */ + ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; + co = expect_trans_n_dim (ci); + + if (expect_failure_with_errno) { + if (proj_errno (T.P)==expect_failure_with_errno) + return another_succeeding_failure (); + fprintf (T.fout, "errno=%d, expected=%d\n", proj_errno (T.P), expect_failure_with_errno); + return another_failing_failure (); + } + + + /* Succeeded in failing? - that's a success */ + if (co.xyz.x==HUGE_VAL) + return another_succeeding_failure (); + + /* Failed to fail? - that's a failure */ + banner (T.operation); + errmsg (3, "%sFailed to fail. Operation definition in line no. %d\n", + delim, (int) T.operation_lineno + ); + return another_failing_failure (); + } + + + if (T.verbosity > 3) { + fprintf (T.fout, "%s\n", T.P->inverted? "INVERTED": "NOT INVERTED"); + fprintf (T.fout, "%s\n", T.dir== 1? "forward": "reverse"); + fprintf (T.fout, "%s\n", proj_angular_input (T.P, T.dir)? "angular in": "linear in"); + fprintf (T.fout, "%s\n", proj_angular_output (T.P, T.dir)? "angular out": "linear out"); + fprintf (T.fout, "left: %d right: %d\n", T.P->left, T.P->right); + } + + tests++; + T.e = parse_coord (args); + if (HUGE_VAL==T.e.v[0]) + return expect_message_cannot_parse (args); + + + /* expected angular values, probably in degrees */ + ce = proj_angular_output (T.P, T.dir)? torad_coord (T.P, T.dir, T.e): T.e; + if (T.verbosity > 3) + fprintf (T.fout, "EXPECTS %.12f %.12f %.12f %.12f\n", + ce.v[0],ce.v[1],ce.v[2],ce.v[3]); + + /* input ("accepted") values, also probably in degrees */ + ci = proj_angular_input (T.P, T.dir)? torad_coord (T.P, T.dir, T.a): T.a; + if (T.verbosity > 3) + fprintf (T.fout, "ACCEPTS %.12f %.12f %.12f %.12f\n", + ci.v[0],ci.v[1],ci.v[2],ci.v[3]); + + /* do the transformation, but mask off dimensions not given in expect-ation */ + co = expect_trans_n_dim (ci); + if (T.dimensions_given < 4) + co.v[3] = 0; + if (T.dimensions_given < 3) + co.v[2] = 0; + + /* angular output from proj_trans comes in radians */ + T.b = proj_angular_output (T.P, T.dir)? todeg_coord (T.P, T.dir, co): co; + if (T.verbosity > 3) + fprintf (T.fout, "GOT %.12f %.12f %.12f %.12f\n", + co.v[0],co.v[1],co.v[2],co.v[3]); + +#if 0 + /* We need to handle unusual axis orders - that'll be an item for version 5.1 */ + if (T.P->axisswap) { + ce = proj_trans (T.P->axisswap, T.dir, ce); + co = proj_trans (T.P->axisswap, T.dir, co); + } +#endif + if (proj_angular_output (T.P, T.dir)) + d = proj_lpz_dist (T.P, ce, co); + else + d = proj_xyz_dist (co, ce); + + if (d > T.tolerance) + return expect_message (d, args); + succs++; + + another_success (); + return 0; +} + + + +/*****************************************************************************/ +static int verbose (const char *args) { +/***************************************************************************** +Tell the system how noisy it should be +******************************************************************************/ + int i = (int) proj_atof (args); + + /* if -q/--quiet flag has been given, we do nothing */ + if (T.verbosity < 0) + return 0; + + if (strlen (args)) + T.verbosity = i; + else + T.verbosity++; + return 0; +} + + +/*****************************************************************************/ +static int echo (const char *args) { +/***************************************************************************** +Add user defined noise to the output stream +******************************************************************************/ +fprintf (T.fout, "%s\n", args); + return 0; +} + + + +/*****************************************************************************/ +static int skip (const char *args) { +/***************************************************************************** +Indicate that the remaining material should be skipped. Mostly for debugging. +******************************************************************************/ + T.skip = 1; + (void) args; + F->level = 2; /* Silence complaints about missing element */ + return 0; +} + + +static int dispatch (const char *cmnd, const char *args) { + if (T.skip) + return SKIP; + if (0==strcmp (cmnd, "operation")) return operation ((char *) args); + if (T.skip_test) + { + if (0==strcmp (cmnd, "expect")) return another_skip(); + return 0; + } + if (0==strcmp (cmnd, "accept")) return accept (args); + if (0==strcmp (cmnd, "expect")) return expect (args); + if (0==strcmp (cmnd, "roundtrip")) return roundtrip (args); + if (0==strcmp (cmnd, "banner")) return banner (args); + if (0==strcmp (cmnd, "verbose")) return verbose (args); + if (0==strcmp (cmnd, "direction")) return direction (args); + if (0==strcmp (cmnd, "tolerance")) return tolerance (args); + if (0==strcmp (cmnd, "ignore")) return ignore (args); + if (0==strcmp (cmnd, "require_grid")) return require_grid (args); + if (0==strcmp (cmnd, "echo")) return echo (args); + if (0==strcmp (cmnd, "skip")) return skip (args); + if (0==strcmp (cmnd, "use_proj4_init_rules")) + return use_proj4_init_rules (args); + + return 0; +} + + + + +struct errno_vs_err_const {const char *the_err_const; int the_errno;}; +static const struct errno_vs_err_const lookup[] = { + {"pjd_err_no_args" , -1}, + {"pjd_err_no_option_in_init_file" , -2}, + {"pjd_err_no_colon_in_init_string" , -3}, + {"pjd_err_proj_not_named" , -4}, + {"pjd_err_unknown_projection_id" , -5}, + {"pjd_err_eccentricity_is_one" , -6}, + {"pjd_err_unknown_unit_id" , -7}, + {"pjd_err_invalid_boolean_param" , -8}, + {"pjd_err_unknown_ellp_param" , -9}, + {"pjd_err_rev_flattening_is_zero" , -10}, + {"pjd_err_ref_rad_larger_than_90" , -11}, + {"pjd_err_es_less_than_zero" , -12}, + {"pjd_err_major_axis_not_given" , -13}, + {"pjd_err_lat_or_lon_exceed_limit" , -14}, + {"pjd_err_invalid_x_or_y" , -15}, + {"pjd_err_wrong_format_dms_value" , -16}, + {"pjd_err_non_conv_inv_meri_dist" , -17}, + {"pjd_err_non_con_inv_phi2" , -18}, + {"pjd_err_acos_asin_arg_too_large" , -19}, + {"pjd_err_tolerance_condition" , -20}, + {"pjd_err_conic_lat_equal" , -21}, + {"pjd_err_lat_larger_than_90" , -22}, + {"pjd_err_lat1_is_zero" , -23}, + {"pjd_err_lat_ts_larger_than_90" , -24}, + {"pjd_err_control_point_no_dist" , -25}, + {"pjd_err_no_rotation_proj" , -26}, + {"pjd_err_w_or_m_zero_or_less" , -27}, + {"pjd_err_lsat_not_in_range" , -28}, + {"pjd_err_path_not_in_range" , -29}, + {"pjd_err_h_less_than_zero" , -30}, + {"pjd_err_k_less_than_zero" , -31}, + {"pjd_err_lat_1_or_2_zero_or_90" , -32}, + {"pjd_err_lat_0_or_alpha_eq_90" , -33}, + {"pjd_err_ellipsoid_use_required" , -34}, + {"pjd_err_invalid_utm_zone" , -35}, + {"pjd_err_tcheby_val_out_of_range" , -36}, + {"pjd_err_failed_to_find_proj" , -37}, + {"pjd_err_failed_to_load_grid" , -38}, + {"pjd_err_invalid_m_or_n" , -39}, + {"pjd_err_n_out_of_range" , -40}, + {"pjd_err_lat_1_2_unspecified" , -41}, + {"pjd_err_abs_lat1_eq_abs_lat2" , -42}, + {"pjd_err_lat_0_half_pi_from_mean" , -43}, + {"pjd_err_unparseable_cs_def" , -44}, + {"pjd_err_geocentric" , -45}, + {"pjd_err_unknown_prime_meridian" , -46}, + {"pjd_err_axis" , -47}, + {"pjd_err_grid_area" , -48}, + {"pjd_err_invalid_sweep_axis" , -49}, + {"pjd_err_malformed_pipeline" , -50}, + {"pjd_err_unit_factor_less_than_0" , -51}, + {"pjd_err_invalid_scale" , -52}, + {"pjd_err_non_convergent" , -53}, + {"pjd_err_missing_args" , -54}, + {"pjd_err_lat_0_is_zero" , -55}, + {"pjd_err_ellipsoidal_unsupported" , -56}, + {"pjd_err_too_many_inits" , -57}, + {"pjd_err_invalid_arg" , -58}, + {"pjd_err_dont_skip" , 5555}, + {"pjd_err_unknown" , 9999}, + {"pjd_err_enomem" , ENOMEM}, +}; + +static const struct errno_vs_err_const unknown = {"PJD_ERR_UNKNOWN", 9999}; + + +static int list_err_codes (void) { + int i; + const int n = sizeof lookup / sizeof lookup[0]; + + for (i = 0; i < n; i++) { + if (9999==lookup[i].the_errno) + break; + fprintf (T.fout, "%25s (%2.2d): %s\n", lookup[i].the_err_const + 8, + lookup[i].the_errno, pj_strerrno(lookup[i].the_errno)); + } + return 0; +} + + +static const char *err_const_from_errno (int err) { + size_t i; + const size_t n = sizeof lookup / sizeof lookup[0]; + + for (i = 0; i < n; i++) { + if (err==lookup[i].the_errno) + return lookup[i].the_err_const + 8; + } + return unknown.the_err_const; +} + + +static int errno_from_err_const (const char *err_const) { + const size_t n = sizeof lookup / sizeof lookup[0]; + size_t i, len; + int ret; + char tolower_err_const[100]; + + /* Make a lower case copy for matching */ + for (i = 0; i < 99; i++) { + if (0==err_const[i] || isspace (err_const[i])) + break; + tolower_err_const[i] = (char) tolower (err_const[i]); + } + tolower_err_const[i] = 0; + + /* If it looks numeric, return that numeric */ + ret = (int) pj_atof (err_const); + if (0!=ret) + return ret; + + /* Else try to find a matching identifier */ + len = strlen (tolower_err_const); + + /* First try to find a match excluding the PJD_ERR_ prefix */ + for (i = 0; i < n; i++) { + if (0==strncmp (lookup[i].the_err_const + 8, err_const, len)) + return lookup[i].the_errno; + } + + /* If that did not work, try with the full name */ + for (i = 0; i < n; i++) { + if (0==strncmp (lookup[i].the_err_const, err_const, len)) + return lookup[i].the_errno; + } + + /* On failure, return something unlikely */ + return 9999; +} + + +static int errmsg (int errlev, const char *msg, ...) { + va_list args; + va_start(args, msg); + vfprintf(stdout, msg, args); + va_end(args); + if (errlev) + errno = errlev; + return errlev; +} + + + + + + + + +/**************************************************************************************** + +FFIO - Flexible format I/O + +FFIO provides functionality for reading proj style instruction strings written +in a less strict format than usual: + +* Whitespace is generally allowed everywhere +* Comments can be written inline, '#' style +* ... or as free format blocks + +The overall mission of FFIO is to facilitate communications of geodetic +parameters and test material in a format that is highly human readable, +and provides ample room for comment, documentation, and test material. + +See the PROJ ".gie" test suites for examples of supported formatting. + +****************************************************************************************/ + + +/***************************************************************************************/ +static ffio *ffio_create (const char **tags, size_t n_tags, size_t max_record_size) { +/**************************************************************************************** +Constructor for the ffio object. +****************************************************************************************/ + ffio *G = static_cast(calloc (1, sizeof (ffio))); + if (0==G) + return 0; + + if (0==max_record_size) + max_record_size = 1000; + + G->args = static_cast(calloc (1, 5*max_record_size)); + if (0==G->args) { + free (G); + return 0; + } + + G->next_args = static_cast(calloc (1, max_record_size)); + if (0==G->args) { + free (G->args); + free (G); + return 0; + } + + G->args_size = 5*max_record_size; + G->next_args_size = max_record_size; + + G->tags = tags; + G->n_tags = n_tags; + return G; +} + + + +/***************************************************************************************/ +static ffio *ffio_destroy (ffio *G) { +/**************************************************************************************** +Free all allocated associated memory, then free G itself. For extra RAII compliancy, +the file object should also be closed if still open, but this will require additional +control logic, and ffio is a gie tool specific package, so we fall back to asserting that +fclose has been called prior to ffio_destroy. +****************************************************************************************/ + free (G->args); + free (G->next_args); + free (G); + return 0; +} + + + +/***************************************************************************************/ +static int at_decorative_element (ffio *G) { +/**************************************************************************************** +A decorative element consists of a line of at least 5 consecutive identical chars, +starting at buffer position 0: +"-----", "=====", "*****", etc. + +A decorative element serves as a end delimiter for the current element, and +continues until a gie command verb is found at the start of a line +****************************************************************************************/ + int i; + char *c; + if (0==G) + return 0; + c = G->next_args; + if (0==c) + return 0; + if (0==c[0]) + return 0; + for (i = 1; i < 5; i++) + if (c[i]!=c[0]) + return 0; + return 1; +} + + + +/***************************************************************************************/ +static const char *at_tag (ffio *G) { +/**************************************************************************************** +A start of a new command serves as an end delimiter for the current command +****************************************************************************************/ + size_t j; + for (j = 0; j < G->n_tags; j++) + if (strncmp (G->next_args, G->tags[j], strlen(G->tags[j]))==0) + return G->tags[j]; + return 0; +} + + + +/***************************************************************************************/ +static int at_end_delimiter (ffio *G) { +/**************************************************************************************** +An instruction consists of everything from its introductory tag to its end +delimiter. An end delimiter can be either the introductory tag of the next +instruction, or a "decorative element", i.e. one of the "ascii art" style +block delimiters typically used to mark up block comments in a free format +file. +****************************************************************************************/ + if (G==0) + return 0; + if (at_decorative_element (G)) + return 1; + if (at_tag (G)) + return 1; + return 0; +} + + + +/***************************************************************************************/ +static int nextline (ffio *G) { +/**************************************************************************************** +Read next line of input file. Returns 1 on success, 0 on failure. +****************************************************************************************/ + G->next_args[0] = 0; + if (T.skip) + return 0; + if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) + return 0; + if (feof (G->f)) + return 0; + pj_chomp (G->next_args); + G->next_lineno++; + return 1; +} + + + +/***************************************************************************************/ +static int locate_tag (ffio *G, const char *tag) { +/**************************************************************************************** +Find start-of-line tag (currently only used to search for for , but any tag +valid). + +Returns 1 on success, 0 on failure. +****************************************************************************************/ + size_t n = strlen (tag); + while (0!=strncmp (tag, G->next_args, n)) + if (0==nextline (G)) + return 0; + return 1; +} + + + +/***************************************************************************************/ +static int step_into_gie_block (ffio *G) { +/**************************************************************************************** +Make sure we're inside a -block. Return 1 on success, 0 otherwise. +****************************************************************************************/ + /* Already inside */ + if (G->level % 2) + return 1; + + if (0==locate_tag (G, "")) + return 0; + + while (0!=strncmp ("", G->next_args, 5)) { + G->next_args[0] = 0; + if (feof (G->f)) + return 0; + if (0==fgets (G->next_args, (int) G->next_args_size - 1, G->f)) + return 0; + pj_chomp (G->next_args); + G->next_lineno++; + } + G->level++; + + /* We're ready at the start - now step into the block */ + return nextline (G); +} + + + +/***************************************************************************************/ +static int skip_to_next_tag (ffio *G) { +/**************************************************************************************** +Skip forward to the next command tag. Return 1 on success, 0 otherwise. +****************************************************************************************/ + const char *c; + if (0==step_into_gie_block (G)) + return 0; + + c = at_tag (G); + + /* If not already there - get there */ + while (!c) { + if (0==nextline (G)) + return 0; + c = at_tag (G); + } + + /* If we reached the end of a block, locate the next and retry */ + if (0==strcmp (c, "")) { + G->level++; + if (feof (G->f)) + return 0; + if (0==step_into_gie_block (G)) + return 0; + G->args[0] = 0; + return skip_to_next_tag (G); + } + G->lineno = G->next_lineno; + + return 1; +} + +/* Add the most recently read line of input to the block already stored. */ +static int append_args (ffio *G) { + size_t skip_chars = 0; + size_t next_len = strlen (G->next_args); + size_t args_len = strlen (G->args); + const char *tag = at_tag (G); + + if (tag) + skip_chars = strlen (tag); + + /* +2: 1 for the space separator and 1 for the NUL termination. */ + if (G->args_size < args_len + next_len - skip_chars + 2) { + char *p = static_cast(realloc (G->args, 2 * G->args_size)); + if (0==p) + return 0; + G->args = p; + G->args_size = 2 * G->args_size; + } + + G->args[args_len] = ' '; + strcpy (G->args + args_len + 1, G->next_args + skip_chars); + + G->next_args[0] = 0; + return 1; +} + + + + + +/***************************************************************************************/ +static int get_inp (ffio *G) { +/**************************************************************************************** +The primary command reader for gie. Reads a block of gie input, cleans up repeated +whitespace etc. The block is stored in G->args. Returns 1 on success, 0 otherwise. +****************************************************************************************/ + G->args[0] = 0; + + if (0==skip_to_next_tag (G)) + return 0; + G->tag = at_tag (G); + + if (0==G->tag) + return 0; + + do { + append_args (G); + if (0==nextline (G)) + return 0; + } while (!at_end_delimiter (G)); + + pj_shrink (G->args); + return 1; +} diff --git a/src/jniproj.c b/src/jniproj.c deleted file mode 100644 index 67aa2478..00000000 --- a/src/jniproj.c +++ /dev/null @@ -1,472 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Java/JNI wrappers for PROJ API. - * Author: Antonello Andrea - * Martin Desruisseaux - * - ****************************************************************************** - * Copyright (c) 2005, Andrea Antonello - * Copyright (c) 2011, Martin Desruisseaux - * Copyright (c) 2018, Even Rouault - * - * 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. - *****************************************************************************/ - -/*! - * \file jniproj.c - * - * \brief - * Functions used by the Java Native Interface (JNI) wrappers of PROJ. - * - * - * \author Antonello Andrea - * \date Wed Oct 20 23:10:24 CEST 2004 - * - * \author Martin Desruisseaux - * \date August 2011 - */ - -#include "proj_config.h" - -#ifdef JNI_ENABLED - -#include -#include -#include "projects.h" -#include "org_proj4_PJ.h" -#include - -#define PJ_FIELD_NAME "ptr" -#define PJ_FIELD_TYPE "J" - -/*! - * \brief - * Internal method returning the address of the PJ structure wrapped by the given Java object. - * This function looks for a field named "ptr" and of type "long" (Java signature "J") in the - * given object. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The address of the PJ structure, or NULL if the operation fails (for example - * because the "ptr" field was not found). - */ -PJ *getPJ(JNIEnv *env, jobject object) -{ - jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); - return (id) ? (PJ*) (*env)->GetLongField(env, object, id) : NULL; -} - -/*! - * \brief - * Internal method returning the java.lang.Double.NaN constant value. - * Efficiency is no a high concern for this particular method, because it - * is used mostly when the user wrongly attempt to use a disposed PJ object. - * - * \param env - The JNI environment. - * \return The java.lang.Double.NaN constant value. - */ -jdouble javaNaN(JNIEnv *env) -{ - jclass c = (*env)->FindClass(env, "java/lang/Double"); - if (c) { // Should never be NULL, but let be paranoiac. - jfieldID id = (*env)->GetStaticFieldID(env, c, "NaN", "D"); - if (id) { // Should never be NULL, but let be paranoiac. - return (*env)->GetStaticDoubleField(env, c, id); - } - } - return 0.0; // Should never happen. -} - -/*! - * \brief - * Returns the Proj4 release number. - * - * \param env - The JNI environment. - * \param class - The class from which this method has been invoked. - * \return The Proj4 release number, or NULL. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion - (JNIEnv *env, jclass class) -{ - const char *desc = pj_get_release(); - return (desc) ? (*env)->NewStringUTF(env, desc) : NULL; -} - -/*! - * \brief - * Allocates a new PJ structure from a definition string. - * - * \param env - The JNI environment. - * \param class - The class from which this method has been invoked. - * \param definition - The string definition to be given to Proj4. - * \return The address of the new PJ structure, or 0 in case of failure. - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ - (JNIEnv *env, jclass class, jstring definition) -{ - const char *def_utf = (*env)->GetStringUTFChars(env, definition, NULL); - if (!def_utf) return 0; /* OutOfMemoryError already thrown. */ - PJ *pj = pj_init_plus(def_utf); - (*env)->ReleaseStringUTFChars(env, definition, def_utf); - return (jlong) pj; -} - -/*! - * \brief - * Allocates a new geographic PJ structure from an existing one. - * - * \param env - The JNI environment. - * \param class - The class from which this method has been invoked. - * \param projected - The PJ object from which to derive a new one. - * \return The address of the new PJ structure, or 0 in case of failure. - */ -JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ - (JNIEnv *env, jclass class, jobject projected) -{ - PJ *pj = getPJ(env, projected); - return (pj) ? (jlong) pj_latlong_from_proj(pj) : 0; -} - -/*! - * \brief - * Returns the definition string. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The definition string. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - char *desc = pj_get_def(pj, 0); - if (desc) { - jstring str = (*env)->NewStringUTF(env, desc); - pj_dalloc(desc); - return str; - } - } - return NULL; -} - -/*! - * \brief - * Returns the description associated to the PJ structure. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The description associated to the PJ structure. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - const char *desc = pj->descr; - if (desc) { - return (*env)->NewStringUTF(env, desc); - } - } - return NULL; -} - -/*! - * \brief - * Returns the CRS type as one of the PJ.Type enum: GEOGRAPHIC, GEOCENTRIC or PROJECTED. - * This function should never return NULL, unless class or fields have been renamed in - * such a way that we can not find anymore the expected enum values. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The CRS type as one of the PJ.Type enum. - */ -JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - const char *type; - if (pj_is_latlong(pj)) { - type = "GEOGRAPHIC"; - } else if (pj_is_geocent(pj)) { - type = "GEOCENTRIC"; - } else { - type = "PROJECTED"; - } - jclass c = (*env)->FindClass(env, "org/proj4/PJ$Type"); - if (c) { - jfieldID id = (*env)->GetStaticFieldID(env, c, type, "Lorg/proj4/PJ$Type;"); - if (id) { - return (*env)->GetStaticObjectField(env, c, id); - } - } - } - return NULL; -} - -/*! - * \brief - * Returns the semi-major axis length. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The semi-major axis length. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return pj ? pj->a_orig : javaNaN(env); -} - -/*! - * \brief - * Computes the semi-minor axis length from the semi-major axis length and the eccentricity - * squared. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The semi-minor axis length. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (!pj) return javaNaN(env); - double a = pj->a_orig; - return sqrt(a*a * (1.0 - pj->es_orig)); -} - -/*! - * \brief - * Returns the eccentricity squared. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The eccentricity. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return pj ? pj->es_orig : javaNaN(env); -} - -/*! - * \brief - * Returns an array of character indicating the direction of each axis. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The axis directions. - */ -JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - int length = strlen(pj->axis); - jcharArray array = (*env)->NewCharArray(env, length); - if (array) { - jchar* axis = (*env)->GetCharArrayElements(env, array, NULL); - if (axis) { - /* Don't use memcp because the type may not be the same. */ - int i; - for (i=0; iaxis[i]; - } - (*env)->ReleaseCharArrayElements(env, array, axis, 0); - } - return array; - } - } - return NULL; -} - -/*! - * \brief - * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The prime meridian longitude, in degrees. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - return (pj) ? (pj->from_greenwich)*(180/M_PI) : javaNaN(env); -} - -/*! - * \brief - * Returns the conversion factor from linear units to metres. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \param vertical - JNI_FALSE for horizontal axes, or JNI_TRUE for the vertical axis. - * \return The conversion factor to metres. - */ -JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre - (JNIEnv *env, jobject object, jboolean vertical) -{ - PJ *pj = getPJ(env, object); - if (pj) { - return (vertical) ? pj->vto_meter : pj->to_meter; - } - return javaNaN(env); -} - -/*! - * \brief - * Converts input values from degrees to radians before coordinate operation, or the output - * values from radians to degrees after the coordinate operation. - * - * \param pj - The PROJ.4 PJ structure. - * \param data - The coordinate array to transform. - * \param numPts - Number of points to transform. - * \param dimension - Dimension of points in the coordinate array. - * \param factor - The scale factor to apply: M_PI/180 for inputs or 180/M_PI for outputs. - */ -void convertAngularOrdinates(PJ *pj, double* data, jint numPts, int dimension, double factor) { - int dimToSkip; - if (pj_is_latlong(pj)) { - /* Convert only the 2 first ordinates and skip all the other dimensions. */ - dimToSkip = dimension - 2; - } else { - /* Not a geographic CRS: nothing to convert. */ - return; - } - double *stop = data + dimension*numPts; - if (dimToSkip > 0) { - while (data != stop) { - (*data++) *= factor; - (*data++) *= factor; - data += dimToSkip; - } - } else { - while (data != stop) { - (*data++) *= factor; - } - } -} - -/*! - * \brief - * Transforms in-place the coordinates in the given array. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \param target - The target CRS. - * \param dimension - The dimension of each coordinate value. Must be equals or greater than 2. - * \param coordinates - The coordinates to transform, as a sequence of (x,y,,...) tuples. - * \param offset - Offset of the first coordinate in the given array. - * \param numPts - Number of points to transform. - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_transform - (JNIEnv *env, jobject object, jobject target, jint dimension, jdoubleArray coordinates, jint offset, jint numPts) -{ - if (!target || !coordinates) { - jclass c = (*env)->FindClass(env, "java/lang/NullPointerException"); - if (c) (*env)->ThrowNew(env, c, "The target CRS and the coordinates array can not be null."); - return; - } - if (dimension < 2 || dimension > org_proj4_PJ_DIMENSION_MAX) { /* Arbitrary upper value for catching potential misuse. */ - jclass c = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); - if (c) (*env)->ThrowNew(env, c, "Illegal number of dimensions."); - return; - } - if ((offset < 0) || (numPts < 0) || (offset + dimension*numPts) > (*env)->GetArrayLength(env, coordinates)) { - jclass c = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException"); - if (c) (*env)->ThrowNew(env, c, "Illegal offset or illegal number of points."); - return; - } - PJ *src_pj = getPJ(env, object); - PJ *dst_pj = getPJ(env, target); - if (src_pj && dst_pj) { - /* Using GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical rather than - GetDoubleArrayElements/ReleaseDoubleArrayElements increase the chances that - the JVM returns direct reference to its internal array without copying data. - However we must promise to run the "critical" code fast, to not make any - system call that may wait for the JVM and to not invoke any other JNI method. */ - double *data = (*env)->GetPrimitiveArrayCritical(env, coordinates, NULL); - if (data) { - double *x = data + offset; - double *y = x + 1; - double *z = (dimension >= 3) ? y+1 : NULL; - convertAngularOrdinates(src_pj, x, numPts, dimension, M_PI/180); - int err = pj_transform(src_pj, dst_pj, numPts, dimension, x, y, z); - convertAngularOrdinates(dst_pj, x, numPts, dimension, 180/M_PI); - (*env)->ReleasePrimitiveArrayCritical(env, coordinates, data, 0); - if (err) { - jclass c = (*env)->FindClass(env, "org/proj4/PJException"); - if (c) (*env)->ThrowNew(env, c, pj_strerrno(err)); - } - } - } -} - -/*! - * \brief - * Returns a description of the last error that occurred, or NULL if none. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - * \return The last error, or NULL. - */ -JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError - (JNIEnv *env, jobject object) -{ - PJ *pj = getPJ(env, object); - if (pj) { - int err = pj_ctx_get_errno(pj->ctx); - if (err) { - return (*env)->NewStringUTF(env, pj_strerrno(err)); - } - } - return NULL; -} - -/*! - * \brief - * Deallocate the PJ structure. This method is invoked by the garbage collector exactly once. - * This method will also set the Java "ptr" final field to 0 as a safety. In theory we are not - * supposed to change the value of a final field. But no Java code should use this field, and - * the PJ object is being garbage collected anyway. We set the field to 0 as a safety in case - * some user invoked the finalize() method explicitly despite our warning in the Javadoc to - * never do such thing. - * - * \param env - The JNI environment. - * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). - */ -JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize - (JNIEnv *env, jobject object) -{ - jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); - if (id) { - PJ *pj = (PJ*) (*env)->GetLongField(env, object, id); - if (pj) { - (*env)->SetLongField(env, object, id, (jlong) 0); - pj_free(pj); - } - } -} - -#endif diff --git a/src/jniproj.cpp b/src/jniproj.cpp new file mode 100644 index 00000000..67aa2478 --- /dev/null +++ b/src/jniproj.cpp @@ -0,0 +1,472 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Java/JNI wrappers for PROJ API. + * Author: Antonello Andrea + * Martin Desruisseaux + * + ****************************************************************************** + * Copyright (c) 2005, Andrea Antonello + * Copyright (c) 2011, Martin Desruisseaux + * Copyright (c) 2018, Even Rouault + * + * 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. + *****************************************************************************/ + +/*! + * \file jniproj.c + * + * \brief + * Functions used by the Java Native Interface (JNI) wrappers of PROJ. + * + * + * \author Antonello Andrea + * \date Wed Oct 20 23:10:24 CEST 2004 + * + * \author Martin Desruisseaux + * \date August 2011 + */ + +#include "proj_config.h" + +#ifdef JNI_ENABLED + +#include +#include +#include "projects.h" +#include "org_proj4_PJ.h" +#include + +#define PJ_FIELD_NAME "ptr" +#define PJ_FIELD_TYPE "J" + +/*! + * \brief + * Internal method returning the address of the PJ structure wrapped by the given Java object. + * This function looks for a field named "ptr" and of type "long" (Java signature "J") in the + * given object. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The address of the PJ structure, or NULL if the operation fails (for example + * because the "ptr" field was not found). + */ +PJ *getPJ(JNIEnv *env, jobject object) +{ + jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); + return (id) ? (PJ*) (*env)->GetLongField(env, object, id) : NULL; +} + +/*! + * \brief + * Internal method returning the java.lang.Double.NaN constant value. + * Efficiency is no a high concern for this particular method, because it + * is used mostly when the user wrongly attempt to use a disposed PJ object. + * + * \param env - The JNI environment. + * \return The java.lang.Double.NaN constant value. + */ +jdouble javaNaN(JNIEnv *env) +{ + jclass c = (*env)->FindClass(env, "java/lang/Double"); + if (c) { // Should never be NULL, but let be paranoiac. + jfieldID id = (*env)->GetStaticFieldID(env, c, "NaN", "D"); + if (id) { // Should never be NULL, but let be paranoiac. + return (*env)->GetStaticDoubleField(env, c, id); + } + } + return 0.0; // Should never happen. +} + +/*! + * \brief + * Returns the Proj4 release number. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \return The Proj4 release number, or NULL. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getVersion + (JNIEnv *env, jclass class) +{ + const char *desc = pj_get_release(); + return (desc) ? (*env)->NewStringUTF(env, desc) : NULL; +} + +/*! + * \brief + * Allocates a new PJ structure from a definition string. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \param definition - The string definition to be given to Proj4. + * \return The address of the new PJ structure, or 0 in case of failure. + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocatePJ + (JNIEnv *env, jclass class, jstring definition) +{ + const char *def_utf = (*env)->GetStringUTFChars(env, definition, NULL); + if (!def_utf) return 0; /* OutOfMemoryError already thrown. */ + PJ *pj = pj_init_plus(def_utf); + (*env)->ReleaseStringUTFChars(env, definition, def_utf); + return (jlong) pj; +} + +/*! + * \brief + * Allocates a new geographic PJ structure from an existing one. + * + * \param env - The JNI environment. + * \param class - The class from which this method has been invoked. + * \param projected - The PJ object from which to derive a new one. + * \return The address of the new PJ structure, or 0 in case of failure. + */ +JNIEXPORT jlong JNICALL Java_org_proj4_PJ_allocateGeoPJ + (JNIEnv *env, jclass class, jobject projected) +{ + PJ *pj = getPJ(env, projected); + return (pj) ? (jlong) pj_latlong_from_proj(pj) : 0; +} + +/*! + * \brief + * Returns the definition string. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The definition string. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getDefinition + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + char *desc = pj_get_def(pj, 0); + if (desc) { + jstring str = (*env)->NewStringUTF(env, desc); + pj_dalloc(desc); + return str; + } + } + return NULL; +} + +/*! + * \brief + * Returns the description associated to the PJ structure. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The description associated to the PJ structure. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_toString + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + const char *desc = pj->descr; + if (desc) { + return (*env)->NewStringUTF(env, desc); + } + } + return NULL; +} + +/*! + * \brief + * Returns the CRS type as one of the PJ.Type enum: GEOGRAPHIC, GEOCENTRIC or PROJECTED. + * This function should never return NULL, unless class or fields have been renamed in + * such a way that we can not find anymore the expected enum values. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The CRS type as one of the PJ.Type enum. + */ +JNIEXPORT jobject JNICALL Java_org_proj4_PJ_getType + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + const char *type; + if (pj_is_latlong(pj)) { + type = "GEOGRAPHIC"; + } else if (pj_is_geocent(pj)) { + type = "GEOCENTRIC"; + } else { + type = "PROJECTED"; + } + jclass c = (*env)->FindClass(env, "org/proj4/PJ$Type"); + if (c) { + jfieldID id = (*env)->GetStaticFieldID(env, c, type, "Lorg/proj4/PJ$Type;"); + if (id) { + return (*env)->GetStaticObjectField(env, c, id); + } + } + } + return NULL; +} + +/*! + * \brief + * Returns the semi-major axis length. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The semi-major axis length. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMajorAxis + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return pj ? pj->a_orig : javaNaN(env); +} + +/*! + * \brief + * Computes the semi-minor axis length from the semi-major axis length and the eccentricity + * squared. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The semi-minor axis length. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getSemiMinorAxis + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (!pj) return javaNaN(env); + double a = pj->a_orig; + return sqrt(a*a * (1.0 - pj->es_orig)); +} + +/*! + * \brief + * Returns the eccentricity squared. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The eccentricity. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getEccentricitySquared + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return pj ? pj->es_orig : javaNaN(env); +} + +/*! + * \brief + * Returns an array of character indicating the direction of each axis. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The axis directions. + */ +JNIEXPORT jcharArray JNICALL Java_org_proj4_PJ_getAxisDirections + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + int length = strlen(pj->axis); + jcharArray array = (*env)->NewCharArray(env, length); + if (array) { + jchar* axis = (*env)->GetCharArrayElements(env, array, NULL); + if (axis) { + /* Don't use memcp because the type may not be the same. */ + int i; + for (i=0; iaxis[i]; + } + (*env)->ReleaseCharArrayElements(env, array, axis, 0); + } + return array; + } + } + return NULL; +} + +/*! + * \brief + * Longitude of the prime meridian measured from the Greenwich meridian, positive eastward. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The prime meridian longitude, in degrees. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getGreenwichLongitude + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + return (pj) ? (pj->from_greenwich)*(180/M_PI) : javaNaN(env); +} + +/*! + * \brief + * Returns the conversion factor from linear units to metres. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \param vertical - JNI_FALSE for horizontal axes, or JNI_TRUE for the vertical axis. + * \return The conversion factor to metres. + */ +JNIEXPORT jdouble JNICALL Java_org_proj4_PJ_getLinearUnitToMetre + (JNIEnv *env, jobject object, jboolean vertical) +{ + PJ *pj = getPJ(env, object); + if (pj) { + return (vertical) ? pj->vto_meter : pj->to_meter; + } + return javaNaN(env); +} + +/*! + * \brief + * Converts input values from degrees to radians before coordinate operation, or the output + * values from radians to degrees after the coordinate operation. + * + * \param pj - The PROJ.4 PJ structure. + * \param data - The coordinate array to transform. + * \param numPts - Number of points to transform. + * \param dimension - Dimension of points in the coordinate array. + * \param factor - The scale factor to apply: M_PI/180 for inputs or 180/M_PI for outputs. + */ +void convertAngularOrdinates(PJ *pj, double* data, jint numPts, int dimension, double factor) { + int dimToSkip; + if (pj_is_latlong(pj)) { + /* Convert only the 2 first ordinates and skip all the other dimensions. */ + dimToSkip = dimension - 2; + } else { + /* Not a geographic CRS: nothing to convert. */ + return; + } + double *stop = data + dimension*numPts; + if (dimToSkip > 0) { + while (data != stop) { + (*data++) *= factor; + (*data++) *= factor; + data += dimToSkip; + } + } else { + while (data != stop) { + (*data++) *= factor; + } + } +} + +/*! + * \brief + * Transforms in-place the coordinates in the given array. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \param target - The target CRS. + * \param dimension - The dimension of each coordinate value. Must be equals or greater than 2. + * \param coordinates - The coordinates to transform, as a sequence of (x,y,,...) tuples. + * \param offset - Offset of the first coordinate in the given array. + * \param numPts - Number of points to transform. + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_transform + (JNIEnv *env, jobject object, jobject target, jint dimension, jdoubleArray coordinates, jint offset, jint numPts) +{ + if (!target || !coordinates) { + jclass c = (*env)->FindClass(env, "java/lang/NullPointerException"); + if (c) (*env)->ThrowNew(env, c, "The target CRS and the coordinates array can not be null."); + return; + } + if (dimension < 2 || dimension > org_proj4_PJ_DIMENSION_MAX) { /* Arbitrary upper value for catching potential misuse. */ + jclass c = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); + if (c) (*env)->ThrowNew(env, c, "Illegal number of dimensions."); + return; + } + if ((offset < 0) || (numPts < 0) || (offset + dimension*numPts) > (*env)->GetArrayLength(env, coordinates)) { + jclass c = (*env)->FindClass(env, "java/lang/ArrayIndexOutOfBoundsException"); + if (c) (*env)->ThrowNew(env, c, "Illegal offset or illegal number of points."); + return; + } + PJ *src_pj = getPJ(env, object); + PJ *dst_pj = getPJ(env, target); + if (src_pj && dst_pj) { + /* Using GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical rather than + GetDoubleArrayElements/ReleaseDoubleArrayElements increase the chances that + the JVM returns direct reference to its internal array without copying data. + However we must promise to run the "critical" code fast, to not make any + system call that may wait for the JVM and to not invoke any other JNI method. */ + double *data = (*env)->GetPrimitiveArrayCritical(env, coordinates, NULL); + if (data) { + double *x = data + offset; + double *y = x + 1; + double *z = (dimension >= 3) ? y+1 : NULL; + convertAngularOrdinates(src_pj, x, numPts, dimension, M_PI/180); + int err = pj_transform(src_pj, dst_pj, numPts, dimension, x, y, z); + convertAngularOrdinates(dst_pj, x, numPts, dimension, 180/M_PI); + (*env)->ReleasePrimitiveArrayCritical(env, coordinates, data, 0); + if (err) { + jclass c = (*env)->FindClass(env, "org/proj4/PJException"); + if (c) (*env)->ThrowNew(env, c, pj_strerrno(err)); + } + } + } +} + +/*! + * \brief + * Returns a description of the last error that occurred, or NULL if none. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + * \return The last error, or NULL. + */ +JNIEXPORT jstring JNICALL Java_org_proj4_PJ_getLastError + (JNIEnv *env, jobject object) +{ + PJ *pj = getPJ(env, object); + if (pj) { + int err = pj_ctx_get_errno(pj->ctx); + if (err) { + return (*env)->NewStringUTF(env, pj_strerrno(err)); + } + } + return NULL; +} + +/*! + * \brief + * Deallocate the PJ structure. This method is invoked by the garbage collector exactly once. + * This method will also set the Java "ptr" final field to 0 as a safety. In theory we are not + * supposed to change the value of a final field. But no Java code should use this field, and + * the PJ object is being garbage collected anyway. We set the field to 0 as a safety in case + * some user invoked the finalize() method explicitly despite our warning in the Javadoc to + * never do such thing. + * + * \param env - The JNI environment. + * \param object - The Java object wrapping the PJ structure (not allowed to be NULL). + */ +JNIEXPORT void JNICALL Java_org_proj4_PJ_finalize + (JNIEnv *env, jobject object) +{ + jfieldID id = (*env)->GetFieldID(env, (*env)->GetObjectClass(env, object), PJ_FIELD_NAME, PJ_FIELD_TYPE); + if (id) { + PJ *pj = (PJ*) (*env)->GetLongField(env, object, id); + if (pj) { + (*env)->SetLongField(env, object, id, (jlong) 0); + pj_free(pj); + } + } +} + +#endif diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index 4f29f4d5..7be6302b 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -55,195 +55,195 @@ endif() ### library source list and include_list ### ############################################## SET(SRC_LIBPROJ_PJ - nad_init.c - PJ_aea.c - PJ_aeqd.c - PJ_affine.c - PJ_airy.c - PJ_aitoff.c - PJ_august.c - PJ_axisswap.c - PJ_bacon.c - PJ_bertin1953.c - PJ_bipc.c - PJ_boggs.c - PJ_bonne.c - PJ_calcofi.c - PJ_cart.c - PJ_cass.c - PJ_cc.c - PJ_ccon.c - PJ_cea.c - PJ_chamb.c - PJ_collg.c - PJ_comill.c - PJ_crast.c - PJ_deformation.c - PJ_denoy.c - PJ_eck1.c - PJ_eck2.c - PJ_eck3.c - PJ_eck4.c - PJ_eck5.c - PJ_eqc.c - PJ_eqdc.c - PJ_eqearth.c - PJ_fahey.c - PJ_fouc_s.c - PJ_gall.c - PJ_geoc.c - PJ_geos.c - PJ_gins8.c - PJ_gnom.c - PJ_gn_sinu.c - PJ_goode.c - PJ_gstmerc.c - PJ_hammer.c - PJ_hatano.c - PJ_helmert.c - PJ_hgridshift.c - PJ_horner.c - PJ_igh.c - PJ_isea.c - PJ_imw_p.c - PJ_krovak.c - PJ_labrd.c - PJ_laea.c - PJ_lagrng.c - PJ_larr.c - PJ_lask.c - PJ_latlong.c - PJ_lcca.c - PJ_lcc.c - PJ_loxim.c - PJ_lsat.c - PJ_misrsom.c - PJ_mbt_fps.c - PJ_mbtfpp.c - PJ_mbtfpq.c - PJ_merc.c - PJ_mill.c - PJ_mod_ster.c - PJ_moll.c - PJ_molodensky.c - PJ_natearth.c - PJ_natearth2.c - PJ_nell.c - PJ_nell_h.c - PJ_nocol.c - PJ_nsper.c - PJ_nzmg.c - PJ_ob_tran.c - PJ_ocea.c - PJ_oea.c - PJ_omerc.c - PJ_ortho.c - PJ_patterson.c - PJ_pipeline.c - PJ_poly.c - PJ_putp2.c - PJ_putp3.c - PJ_putp4p.c - PJ_putp5.c - PJ_putp6.c - PJ_qsc.c - PJ_robin.c - PJ_rpoly.c - PJ_sch.c - PJ_sconics.c - PJ_somerc.c - PJ_sterea.c - PJ_stere.c - PJ_sts.c - PJ_tcc.c - PJ_tcea.c - PJ_times.c - PJ_tmerc.c - PJ_tobmerc.c - PJ_tpeqd.c - PJ_unitconvert.c - PJ_urm5.c - PJ_urmfps.c - PJ_vandg.c - PJ_vandg2.c - PJ_vandg4.c - PJ_vgridshift.c - PJ_wag2.c - PJ_wag3.c - PJ_wag7.c - PJ_wink1.c - PJ_wink2.c - proj_etmerc.c + nad_init.cpp + PJ_aea.cpp + PJ_aeqd.cpp + PJ_affine.cpp + PJ_airy.cpp + PJ_aitoff.cpp + PJ_august.cpp + PJ_axisswap.cpp + PJ_bacon.cpp + PJ_bertin1953.cpp + PJ_bipc.cpp + PJ_boggs.cpp + PJ_bonne.cpp + PJ_calcofi.cpp + PJ_cart.cpp + PJ_cass.cpp + PJ_cc.cpp + PJ_ccon.cpp + PJ_cea.cpp + PJ_chamb.cpp + PJ_collg.cpp + PJ_comill.cpp + PJ_crast.cpp + PJ_deformation.cpp + PJ_denoy.cpp + PJ_eck1.cpp + PJ_eck2.cpp + PJ_eck3.cpp + PJ_eck4.cpp + PJ_eck5.cpp + PJ_eqc.cpp + PJ_eqdc.cpp + PJ_eqearth.cpp + PJ_fahey.cpp + PJ_fouc_s.cpp + PJ_gall.cpp + PJ_geoc.cpp + PJ_geos.cpp + PJ_gins8.cpp + PJ_gnom.cpp + PJ_gn_sinu.cpp + PJ_goode.cpp + PJ_gstmerc.cpp + PJ_hammer.cpp + PJ_hatano.cpp + PJ_helmert.cpp + PJ_hgridshift.cpp + PJ_horner.cpp + PJ_igh.cpp + PJ_isea.cpp + PJ_imw_p.cpp + PJ_krovak.cpp + PJ_labrd.cpp + PJ_laea.cpp + PJ_lagrng.cpp + PJ_larr.cpp + PJ_lask.cpp + PJ_latlong.cpp + PJ_lcca.cpp + PJ_lcc.cpp + PJ_loxim.cpp + PJ_lsat.cpp + PJ_misrsom.cpp + PJ_mbt_fps.cpp + PJ_mbtfpp.cpp + PJ_mbtfpq.cpp + PJ_merc.cpp + PJ_mill.cpp + PJ_mod_ster.cpp + PJ_moll.cpp + PJ_molodensky.cpp + PJ_natearth.cpp + PJ_natearth2.cpp + PJ_nell.cpp + PJ_nell_h.cpp + PJ_nocol.cpp + PJ_nsper.cpp + PJ_nzmg.cpp + PJ_ob_tran.cpp + PJ_ocea.cpp + PJ_oea.cpp + PJ_omerc.cpp + PJ_ortho.cpp + PJ_patterson.cpp + PJ_pipeline.cpp + PJ_poly.cpp + PJ_putp2.cpp + PJ_putp3.cpp + PJ_putp4p.cpp + PJ_putp5.cpp + PJ_putp6.cpp + PJ_qsc.cpp + PJ_robin.cpp + PJ_rpoly.cpp + PJ_sch.cpp + PJ_sconics.cpp + PJ_somerc.cpp + PJ_sterea.cpp + PJ_stere.cpp + PJ_sts.cpp + PJ_tcc.cpp + PJ_tcea.cpp + PJ_times.cpp + PJ_tmerc.cpp + PJ_tobmerc.cpp + PJ_tpeqd.cpp + PJ_unitconvert.cpp + PJ_urm5.cpp + PJ_urmfps.cpp + PJ_vandg.cpp + PJ_vandg2.cpp + PJ_vandg4.cpp + PJ_vgridshift.cpp + PJ_wag2.cpp + PJ_wag3.cpp + PJ_wag7.cpp + PJ_wink1.cpp + PJ_wink2.cpp + proj_etmerc.cpp ) SET(SRC_LIBPROJ_CORE - aasincos.c - adjlon.c - bch2bps.c - bchgen.c - biveval.c - dmstor.c - emess.c + aasincos.cpp + adjlon.cpp + bch2bps.cpp + bchgen.cpp + biveval.cpp + dmstor.cpp + emess.cpp emess.h - geocent.c + geocent.cpp geocent.h - geodesic.c - mk_cheby.c - nad_cvt.c - nad_init.c - nad_intr.c - pj_apply_gridshift.c - pj_apply_vgridshift.c - pj_auth.c - pj_ctx.c - pj_fileapi.c - pj_datum_set.c - pj_datums.c - pj_deriv.c - pj_ell_set.c - pj_ellps.c - pj_errno.c - pj_factors.c - pj_fwd.c - pj_gauss.c - pj_gc_reader.c - pj_geocent.c - pj_gridcatalog.c - pj_gridinfo.c - pj_gridlist.c - PJ_healpix.c - pj_init.c - pj_initcache.c - pj_inv.c - pj_list.c + geodesic.cpp + mk_cheby.cpp + nad_cvt.cpp + nad_init.cpp + nad_intr.cpp + pj_apply_gridshift.cpp + pj_apply_vgridshift.cpp + pj_auth.cpp + pj_ctx.cpp + pj_fileapi.cpp + pj_datum_set.cpp + pj_datums.cpp + pj_deriv.cpp + pj_ell_set.cpp + pj_ellps.cpp + pj_errno.cpp + pj_factors.cpp + pj_fwd.cpp + pj_gauss.cpp + pj_gc_reader.cpp + pj_geocent.cpp + pj_gridcatalog.cpp + pj_gridinfo.cpp + pj_gridlist.cpp + PJ_healpix.cpp + pj_init.cpp + pj_initcache.cpp + pj_inv.cpp + pj_list.cpp pj_list.h - pj_log.c - pj_malloc.c - pj_math.c - pj_mlfn.c - pj_msfn.c - pj_mutex.c - proj_4D_api.c - pj_internal.c + pj_log.cpp + pj_malloc.cpp + pj_math.cpp + pj_mlfn.cpp + pj_msfn.cpp + pj_mutex.cpp + proj_4D_api.cpp + pj_internal.cpp proj_internal.h - pj_open_lib.c - pj_param.c - pj_phi2.c - pj_pr_list.c - pj_qsfn.c - pj_release.c - pj_strerrno.c - pj_transform.c - pj_tsfn.c - pj_units.c - pj_utils.c - pj_zpoly1.c - proj_mdist.c + pj_open_lib.cpp + pj_param.cpp + pj_phi2.cpp + pj_pr_list.cpp + pj_qsfn.cpp + pj_release.cpp + pj_strerrno.cpp + pj_transform.cpp + pj_tsfn.cpp + pj_units.cpp + pj_utils.cpp + pj_zpoly1.cpp + proj_mdist.cpp proj_math.h - proj_rouss.c - rtodms.c - vector1.c - pj_strtod.c + proj_rouss.cpp + rtodms.cpp + vector1.cpp + pj_strtod.cpp pj_wkt1_generated_parser.c pj_wkt2_generated_parser.c ${CMAKE_CURRENT_BINARY_DIR}/proj_config.h @@ -301,7 +301,7 @@ endif(JNI_SUPPORT AND NOT JNI_FOUND) boost_report_value(JNI_SUPPORT) if(JNI_SUPPORT) set(SRC_LIBPROJ_CORE ${SRC_LIBPROJ_CORE} - jniproj.c ) + jniproj.cpp ) set(HEADERS_LIBPROJ ${HEADERS_LIBPROJ} org_proj4_PJ.h) source_group("Source Files\\JNI" FILES ${SRC_LIBPROJ_JNI}) diff --git a/src/mk_cheby.c b/src/mk_cheby.c deleted file mode 100644 index a2f90bef..00000000 --- a/src/mk_cheby.c +++ /dev/null @@ -1,192 +0,0 @@ -#include "projects.h" -static void /* sum coefficients less than res */ -eval(projUV **w, int nu, int nv, double res, projUV *resid) { - int i, j; - double ab; - projUV *s; - - resid->u = resid->v = 0.; - for (i = 0; i < nu; ++i) { - s = w[i]; - for (j = 0; j < nv; ++j) { - if ((ab = fabs(s->u)) < res) - resid->u += ab; - if ((ab = fabs(s->v)) < res) - resid->v += ab; - ++s; - } - } -} -static Tseries * /* create power series structure */ -makeT(int nru, int nrv) { - Tseries *T; - int i; - - if (!(T = (Tseries *)pj_malloc(sizeof(Tseries)))) - return 0; - if (!(T->cu = (struct PW_COEF *)pj_malloc(sizeof(struct PW_COEF) * nru))) { - pj_dalloc(T); - return 0; - } - if (!(T->cv = (struct PW_COEF *)pj_malloc(sizeof(struct PW_COEF) * nrv))) { - pj_dalloc(T->cu); - pj_dalloc(T); - return 0; - } - - for (i = 0; i < nru; ++i) - T->cu[i].c = 0; - for (i = 0; i < nrv; ++i) - T->cv[i].c = 0; - return T; -} -Tseries * -mk_cheby(projUV a, projUV b, double res, projUV *resid, projUV (*func)(projUV), - int nu, int nv, int power) { - int j, i, nru, nrv, *ncu, *ncv; - Tseries *T = NULL; - projUV **w; - double cutres; - - if (!(w = (projUV **)vector2(nu, nv, sizeof(projUV)))) - return 0; - if (!(ncu = (int *)vector1(nu + nv, sizeof(int)))) { - freev2((void **)w, nu); - return 0; - } - ncv = ncu + nu; - if (!bchgen(a, b, nu, nv, w, func)) { - projUV *s; - double ab, *p; - - /* analyse coefficients and adjust until residual OK */ - cutres = res; - for (i = 4; i ; --i) { - eval(w, nu, nv, cutres, resid); - if (resid->u < res && resid->v < res) - break; - cutres *= 0.5; - } - if (i <= 0) /* warn of too many tries */ - resid->u = - resid->u; - /* apply cut resolution and set pointers */ - nru = nrv = 0; - for (j = 0; j < nu; ++j) { - ncu[j] = ncv[j] = 0; /* clear column maxes */ - s = w[j]; - for (i = 0; i < nv; ++i) { - if ((ab = fabs(s->u)) < cutres) /* < resolution ? */ - s->u = 0.; /* clear coefficient */ - else - ncu[j] = i + 1; /* update column max */ - if ((ab = fabs(s->v)) < cutres) /* same for v coef's */ - s->v = 0.; - else - ncv[j] = i + 1; - ++s; - } - if (ncu[j]) nru = j + 1; /* update row max */ - if (ncv[j]) nrv = j + 1; - } - if (power) { /* convert to bivariate power series */ - if (!bch2bps(a, b, w, nu, nv)) - goto error; - /* possible change in some row counts, so readjust */ - nru = nrv = 0; - for (j = 0; j < nu; ++j) { - ncu[j] = ncv[j] = 0; /* clear column maxes */ - s = w[j]; - for (i = 0; i < nv; ++i) { - if (s->u != 0.0) - ncu[j] = i + 1; /* update column max */ - if (s->v != 0.0) - ncv[j] = i + 1; - ++s; - } - if (ncu[j]) nru = j + 1; /* update row max */ - if (ncv[j]) nrv = j + 1; - } - if ((T = makeT(nru, nrv)) != NULL ) { - T->a = a; - T->b = b; - T->mu = nru - 1; - T->mv = nrv - 1; - T->power = 1; - for (i = 0; i < nru; ++i) /* store coefficient rows for u */ - { - if ((T->cu[i].m = ncu[i]) != 0) - { - if ((p = T->cu[i].c = - (double *)pj_malloc(sizeof(double) * ncu[i]))) - for (j = 0; j < ncu[i]; ++j) - *p++ = (w[i] + j)->u; - else - goto error; - } - } - for (i = 0; i < nrv; ++i) /* same for v */ - { - if ((T->cv[i].m = ncv[i]) != 0) - { - if ((p = T->cv[i].c = - (double *)pj_malloc(sizeof(double) * ncv[i]))) - for (j = 0; j < ncv[i]; ++j) - *p++ = (w[i] + j)->v; - else - goto error; - } - } - } - } else if ((T = makeT(nru, nrv)) != NULL) { - /* else make returned Chebyshev coefficient structure */ - T->mu = nru - 1; /* save row degree */ - T->mv = nrv - 1; - T->a.u = a.u + b.u; /* set argument scaling */ - T->a.v = a.v + b.v; - T->b.u = 1. / (b.u - a.u); - T->b.v = 1. / (b.v - a.v); - T->power = 0; - for (i = 0; i < nru; ++i) /* store coefficient rows for u */ - { - if ((T->cu[i].m = ncu[i]) != 0) - { - if ((p = T->cu[i].c = - (double *)pj_malloc(sizeof(double) * ncu[i]))) - for (j = 0; j < ncu[i]; ++j) - *p++ = (w[i] + j)->u; - else - goto error; - } - } - for (i = 0; i < nrv; ++i) /* same for v */ - { - if ((T->cv[i].m = ncv[i]) != 0) - { - if ((p = T->cv[i].c = - (double *)pj_malloc(sizeof(double) * ncv[i]))) - for (j = 0; j < ncv[i]; ++j) - *p++ = (w[i] + j)->v; - else - goto error; - } - } - } else - goto error; - } - goto gohome; - error: - if (T) { /* pj_dalloc up possible allocations */ - for (i = 0; i <= T->mu; ++i) - if (T->cu[i].c) - pj_dalloc(T->cu[i].c); - for (i = 0; i <= T->mv; ++i) - if (T->cv[i].c) - pj_dalloc(T->cv[i].c); - pj_dalloc(T); - } - T = 0; - gohome: - freev2((void **) w, nu); - pj_dalloc(ncu); - return T; -} diff --git a/src/mk_cheby.cpp b/src/mk_cheby.cpp new file mode 100644 index 00000000..a2f90bef --- /dev/null +++ b/src/mk_cheby.cpp @@ -0,0 +1,192 @@ +#include "projects.h" +static void /* sum coefficients less than res */ +eval(projUV **w, int nu, int nv, double res, projUV *resid) { + int i, j; + double ab; + projUV *s; + + resid->u = resid->v = 0.; + for (i = 0; i < nu; ++i) { + s = w[i]; + for (j = 0; j < nv; ++j) { + if ((ab = fabs(s->u)) < res) + resid->u += ab; + if ((ab = fabs(s->v)) < res) + resid->v += ab; + ++s; + } + } +} +static Tseries * /* create power series structure */ +makeT(int nru, int nrv) { + Tseries *T; + int i; + + if (!(T = (Tseries *)pj_malloc(sizeof(Tseries)))) + return 0; + if (!(T->cu = (struct PW_COEF *)pj_malloc(sizeof(struct PW_COEF) * nru))) { + pj_dalloc(T); + return 0; + } + if (!(T->cv = (struct PW_COEF *)pj_malloc(sizeof(struct PW_COEF) * nrv))) { + pj_dalloc(T->cu); + pj_dalloc(T); + return 0; + } + + for (i = 0; i < nru; ++i) + T->cu[i].c = 0; + for (i = 0; i < nrv; ++i) + T->cv[i].c = 0; + return T; +} +Tseries * +mk_cheby(projUV a, projUV b, double res, projUV *resid, projUV (*func)(projUV), + int nu, int nv, int power) { + int j, i, nru, nrv, *ncu, *ncv; + Tseries *T = NULL; + projUV **w; + double cutres; + + if (!(w = (projUV **)vector2(nu, nv, sizeof(projUV)))) + return 0; + if (!(ncu = (int *)vector1(nu + nv, sizeof(int)))) { + freev2((void **)w, nu); + return 0; + } + ncv = ncu + nu; + if (!bchgen(a, b, nu, nv, w, func)) { + projUV *s; + double ab, *p; + + /* analyse coefficients and adjust until residual OK */ + cutres = res; + for (i = 4; i ; --i) { + eval(w, nu, nv, cutres, resid); + if (resid->u < res && resid->v < res) + break; + cutres *= 0.5; + } + if (i <= 0) /* warn of too many tries */ + resid->u = - resid->u; + /* apply cut resolution and set pointers */ + nru = nrv = 0; + for (j = 0; j < nu; ++j) { + ncu[j] = ncv[j] = 0; /* clear column maxes */ + s = w[j]; + for (i = 0; i < nv; ++i) { + if ((ab = fabs(s->u)) < cutres) /* < resolution ? */ + s->u = 0.; /* clear coefficient */ + else + ncu[j] = i + 1; /* update column max */ + if ((ab = fabs(s->v)) < cutres) /* same for v coef's */ + s->v = 0.; + else + ncv[j] = i + 1; + ++s; + } + if (ncu[j]) nru = j + 1; /* update row max */ + if (ncv[j]) nrv = j + 1; + } + if (power) { /* convert to bivariate power series */ + if (!bch2bps(a, b, w, nu, nv)) + goto error; + /* possible change in some row counts, so readjust */ + nru = nrv = 0; + for (j = 0; j < nu; ++j) { + ncu[j] = ncv[j] = 0; /* clear column maxes */ + s = w[j]; + for (i = 0; i < nv; ++i) { + if (s->u != 0.0) + ncu[j] = i + 1; /* update column max */ + if (s->v != 0.0) + ncv[j] = i + 1; + ++s; + } + if (ncu[j]) nru = j + 1; /* update row max */ + if (ncv[j]) nrv = j + 1; + } + if ((T = makeT(nru, nrv)) != NULL ) { + T->a = a; + T->b = b; + T->mu = nru - 1; + T->mv = nrv - 1; + T->power = 1; + for (i = 0; i < nru; ++i) /* store coefficient rows for u */ + { + if ((T->cu[i].m = ncu[i]) != 0) + { + if ((p = T->cu[i].c = + (double *)pj_malloc(sizeof(double) * ncu[i]))) + for (j = 0; j < ncu[i]; ++j) + *p++ = (w[i] + j)->u; + else + goto error; + } + } + for (i = 0; i < nrv; ++i) /* same for v */ + { + if ((T->cv[i].m = ncv[i]) != 0) + { + if ((p = T->cv[i].c = + (double *)pj_malloc(sizeof(double) * ncv[i]))) + for (j = 0; j < ncv[i]; ++j) + *p++ = (w[i] + j)->v; + else + goto error; + } + } + } + } else if ((T = makeT(nru, nrv)) != NULL) { + /* else make returned Chebyshev coefficient structure */ + T->mu = nru - 1; /* save row degree */ + T->mv = nrv - 1; + T->a.u = a.u + b.u; /* set argument scaling */ + T->a.v = a.v + b.v; + T->b.u = 1. / (b.u - a.u); + T->b.v = 1. / (b.v - a.v); + T->power = 0; + for (i = 0; i < nru; ++i) /* store coefficient rows for u */ + { + if ((T->cu[i].m = ncu[i]) != 0) + { + if ((p = T->cu[i].c = + (double *)pj_malloc(sizeof(double) * ncu[i]))) + for (j = 0; j < ncu[i]; ++j) + *p++ = (w[i] + j)->u; + else + goto error; + } + } + for (i = 0; i < nrv; ++i) /* same for v */ + { + if ((T->cv[i].m = ncv[i]) != 0) + { + if ((p = T->cv[i].c = + (double *)pj_malloc(sizeof(double) * ncv[i]))) + for (j = 0; j < ncv[i]; ++j) + *p++ = (w[i] + j)->v; + else + goto error; + } + } + } else + goto error; + } + goto gohome; + error: + if (T) { /* pj_dalloc up possible allocations */ + for (i = 0; i <= T->mu; ++i) + if (T->cu[i].c) + pj_dalloc(T->cu[i].c); + for (i = 0; i <= T->mv; ++i) + if (T->cv[i].c) + pj_dalloc(T->cv[i].c); + pj_dalloc(T); + } + T = 0; + gohome: + freev2((void **) w, nu); + pj_dalloc(ncu); + return T; +} diff --git a/src/multistresstest.c b/src/multistresstest.c deleted file mode 100644 index b0bd5c9c..00000000 --- a/src/multistresstest.c +++ /dev/null @@ -1,488 +0,0 @@ -/****************************************************************************** - * - * Project: PROJ.4 - * Purpose: Mainline program to stress test multithreaded PROJ.4 processing. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2010, Frank Warmerdam - * - * 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 -#include -#include - -#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#endif - -#include "proj_api.h" - -#ifdef _WIN32 - #include -#else - #include - #include -#endif - -#define num_threads 10 -static int num_iterations = 1000000; -static int reinit_every_iteration=0; -static int add_no_defs = 0; - -typedef struct { - const char *src_def; - const char *dst_def; - - double src_x, src_y, src_z; - double dst_x, dst_y, dst_z; - - int dst_error; - int skip; -} TestItem; - -static TestItem test_list[] = { - { - "+proj=utm +zone=11 +datum=WGS84", - "+proj=latlong +datum=WGS84", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=utm +zone=11 +datum=NAD83", - "+proj=latlong +datum=NAD27", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=utm +zone=11 +datum=NAD83", - "+proj=latlong +nadgrids=@null +ellps=WGS84", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=utm +zone=11 +datum=WGS84", - "+proj=merc +datum=potsdam", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=latlong +nadgrids=nzgd2kgrid0005.gsb", - "+proj=latlong +datum=WGS84", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=latlong +nadgrids=nzgd2kgrid0005.gsb", - "+proj=latlong +datum=WGS84", - 170 * DEG_TO_RAD, -40 * DEG_TO_RAD, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=latlong +ellps=GRS80 +towgs84=2,3,5", - "+proj=latlong +ellps=intl +towgs84=10,12,15", - 170 * DEG_TO_RAD, -40 * DEG_TO_RAD, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=eqc +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - "+proj=stere +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=cea +lat_ts=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=merc +lon_0=12 +k=0.999 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=bonne +lat_1=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=cass +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=nzmg +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=gnom +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=ortho +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=laea +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=aeqd +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=eqdc +lat_1=20 +lat_2=5 +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+proj=mill +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", - "+proj=moll +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", - 150000.0, 250000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - "+init=epsg:3309", - "+init=epsg:4326", - 150000.0, 30000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - }, - { - /* Bad projection (invalid ellipsoid parameter +R_A=0) */ - "+proj=utm +zone=11 +datum=WGS84", - "+proj=merc +datum=potsdam +R_A=0", - 150000.0, 3000000.0, 0.0, - 0.0, 0.0, 0.0, - 0, 0 - } -}; - -static volatile int active_thread_count = 0; - -static projPJ* custom_pj_init_plus_ctx(projCtx ctx, const char* def) -{ - if( add_no_defs ) - { - char szBuffer[256]; - strcpy(szBuffer, def); - strcat(szBuffer, " +no_defs"); - return pj_init_plus_ctx(ctx, szBuffer); - } - else - return pj_init_plus_ctx(ctx, def); -} - -/************************************************************************/ -/* TestThread() */ -/************************************************************************/ - -static void TestThread() - -{ - int i, test_count = sizeof(test_list) / sizeof(TestItem); - int repeat_count = num_iterations; - int i_iter; - -/* -------------------------------------------------------------------- */ -/* Initialize coordinate system definitions. */ -/* -------------------------------------------------------------------- */ - projPJ *src_pj_list, *dst_pj_list; - projCtx ctx = pj_ctx_alloc(); - - src_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); - dst_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); - - if(!reinit_every_iteration) - { - for( i = 0; i < test_count; i++ ) - { - TestItem *test = test_list + i; - - src_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->src_def ); - dst_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->dst_def ); - } - } - -/* -------------------------------------------------------------------- */ -/* Perform tests - over and over. */ -/* -------------------------------------------------------------------- */ - - for( i_iter = 0; i_iter < repeat_count; i_iter++ ) - { - for( i = 0; i < test_count; i++ ) - { - TestItem *test = test_list + i; - double x, y, z; - int error; - - x = test->src_x; - y = test->src_y; - z = test->src_z; - - if( reinit_every_iteration ) - { - src_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->src_def ); - dst_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->dst_def ); - - { - int skipTest = (src_pj_list[i] == NULL || dst_pj_list[i] == NULL); - - if ( skipTest != test->skip ) - fprintf( stderr, "Threaded projection initialization does not match unthreaded initialization\n" ); - - if (skipTest) - { - pj_free( src_pj_list[i] ); - pj_free( dst_pj_list[i] ); - continue; - } - } - } - - if ( test->skip ) - continue; - - error = pj_transform( src_pj_list[i], dst_pj_list[i], 1, 0, - &x, &y, &z ); - - - if( error != test->dst_error ) - { - fprintf( stderr, "Got error %d, expected %d\n", - error, test->dst_error ); - } - - if( x != test->dst_x || y != test->dst_y || z != test->dst_z ) - { - fprintf( stderr, - "Got %.15g,%.15g,%.15g\n" - "Expected %.15g,%.15g,%.15g\n" - "Diff %.15g,%.15g,%.15g\n", - x, y, z, - test->dst_x, test->dst_y, test->dst_z, - x-test->dst_x, y-test->dst_y, z-test->dst_z); - } - - if( reinit_every_iteration ) - { - pj_free( src_pj_list[i] ); - pj_free( dst_pj_list[i] ); - } - } - } - -/* -------------------------------------------------------------------- */ -/* Cleanup */ -/* -------------------------------------------------------------------- */ - if( !reinit_every_iteration ) - { - for( i = 0; i < test_count; i++ ) - { - pj_free( src_pj_list[i] ); - pj_free( dst_pj_list[i] ); - } - } - - free( src_pj_list ); - free( dst_pj_list ); - - pj_ctx_free( ctx ); - - printf( "%d iterations of the %d tests complete in thread X\n", - repeat_count, test_count ); - - active_thread_count--; -} - -#ifdef _WIN32 -/************************************************************************/ -/* WinTestThread() */ -/************************************************************************/ - -static DWORD WINAPI WinTestThread( LPVOID lpParameter ) - -{ - TestThread(); - - return 0; -} - -#else -/************************************************************************/ -/* PosixTestThread() */ -/************************************************************************/ - -static void *PosixTestThread( void *pData ) - -{ - (void)pData; - TestThread(); - return NULL; -} -#endif - -/************************************************************************/ -/* main() */ -/************************************************************************/ -#ifdef _WIN32 -static DWORD WINAPI do_main( LPVOID unused ) -#else -static int do_main(void) -#endif -{ -/* -------------------------------------------------------------------- */ -/* Our first pass is to establish the correct answers for all */ -/* the tests. */ -/* -------------------------------------------------------------------- */ - int i, test_count = sizeof(test_list) / sizeof(TestItem); - - for( i = 0; i < test_count; i++ ) - { - TestItem *test = test_list + i; - - projPJ src_pj, dst_pj; - - src_pj = custom_pj_init_plus_ctx( pj_get_default_ctx(), test->src_def ); - dst_pj = custom_pj_init_plus_ctx( pj_get_default_ctx(), test->dst_def ); - - if( src_pj == NULL ) - { - printf( "Unable to translate:\n%s\n", test->src_def ); - test->skip = 1; - pj_free (dst_pj); - continue; - } - - if( dst_pj == NULL ) - { - printf( "Unable to translate:\n%s\n", test->dst_def ); - test->skip = 1; - pj_free (src_pj); - continue; - } - - test->dst_x = test->src_x; - test->dst_y = test->src_y; - test->dst_z = test->src_z; - - test->dst_error = pj_transform( src_pj, dst_pj, 1, 0, - &(test->dst_x), - &(test->dst_y), - &(test->dst_z) ); - - pj_free( src_pj ); - pj_free( dst_pj ); - - test->skip = 0; - -#ifdef notdef - printf( "Test %d - output %.14g,%.14g,%g\n", i, test->dst_x, test->dst_y, test->dst_z ); -#endif - } - - printf( "%d tests initialized.\n", test_count ); - -/* -------------------------------------------------------------------- */ -/* Now launch a bunch of threads to repeat the tests. */ -/* -------------------------------------------------------------------- */ -#ifdef _WIN32 - - { //Scoped to workaround lack of c99 support in VS - HANDLE ahThread[num_threads]; - - for( i = 0; i < num_threads; i++ ) - { - active_thread_count++; - - ahThread[i] = CreateThread(NULL, 0, WinTestThread, NULL, 0, NULL); - - if (ahThread[i] == 0) - { - printf( "Thread creation failed."); - return 1; - } - } - - printf( "%d test threads launched.\n", num_threads ); - - WaitForMultipleObjects(num_threads, ahThread, TRUE, INFINITE); - } - -#else - { - pthread_t ahThread[num_threads]; - pthread_attr_t hThreadAttr; - - pthread_attr_init( &hThreadAttr ); - pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); - - for( i = 0; i < num_threads; i++ ) - { - active_thread_count++; - - pthread_create( &(ahThread[i]), &hThreadAttr, - PosixTestThread, NULL ); - } - - printf( "%d test threads launched.\n", num_threads ); - - while( active_thread_count > 0 ) - sleep( 1 ); - } -#endif - - printf( "all tests complete.\n" ); - - return 0; -} - - -int main( int argc, char **argv ) -{ - int i; - for(i=0;i +#include +#include + +#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#endif + +#include "proj_api.h" + +#ifdef _WIN32 + #include +#else + #include + #include +#endif + +#define num_threads 10 +static int num_iterations = 1000000; +static int reinit_every_iteration=0; +static int add_no_defs = 0; + +typedef struct { + const char *src_def; + const char *dst_def; + + double src_x, src_y, src_z; + double dst_x, dst_y, dst_z; + + int dst_error; + int skip; +} TestItem; + +static TestItem test_list[] = { + { + "+proj=utm +zone=11 +datum=WGS84", + "+proj=latlong +datum=WGS84", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=utm +zone=11 +datum=NAD83", + "+proj=latlong +datum=NAD27", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=utm +zone=11 +datum=NAD83", + "+proj=latlong +nadgrids=@null +ellps=WGS84", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=utm +zone=11 +datum=WGS84", + "+proj=merc +datum=potsdam", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=latlong +nadgrids=nzgd2kgrid0005.gsb", + "+proj=latlong +datum=WGS84", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=latlong +nadgrids=nzgd2kgrid0005.gsb", + "+proj=latlong +datum=WGS84", + 170 * DEG_TO_RAD, -40 * DEG_TO_RAD, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=latlong +ellps=GRS80 +towgs84=2,3,5", + "+proj=latlong +ellps=intl +towgs84=10,12,15", + 170 * DEG_TO_RAD, -40 * DEG_TO_RAD, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=eqc +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + "+proj=stere +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=cea +lat_ts=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=merc +lon_0=12 +k=0.999 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=bonne +lat_1=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=cass +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=nzmg +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=gnom +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=ortho +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=laea +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=aeqd +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=eqdc +lat_1=20 +lat_2=5 +lat_0=11 +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+proj=mill +lat_0=11 +lon_0=12 +y_0=200000 +datum=WGS84 ", + "+proj=moll +lon_0=12 +x_0=100000 +y_0=200000 +datum=WGS84 ", + 150000.0, 250000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + "+init=epsg:3309", + "+init=epsg:4326", + 150000.0, 30000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + }, + { + /* Bad projection (invalid ellipsoid parameter +R_A=0) */ + "+proj=utm +zone=11 +datum=WGS84", + "+proj=merc +datum=potsdam +R_A=0", + 150000.0, 3000000.0, 0.0, + 0.0, 0.0, 0.0, + 0, 0 + } +}; + +static volatile int active_thread_count = 0; + +static projPJ custom_pj_init_plus_ctx(projCtx ctx, const char* def) +{ + if( add_no_defs ) + { + char szBuffer[256]; + strcpy(szBuffer, def); + strcat(szBuffer, " +no_defs"); + return pj_init_plus_ctx(ctx, szBuffer); + } + else + return pj_init_plus_ctx(ctx, def); +} + +/************************************************************************/ +/* TestThread() */ +/************************************************************************/ + +static void TestThread() + +{ + int i, test_count = sizeof(test_list) / sizeof(TestItem); + int repeat_count = num_iterations; + int i_iter; + +/* -------------------------------------------------------------------- */ +/* Initialize coordinate system definitions. */ +/* -------------------------------------------------------------------- */ + projPJ *src_pj_list, *dst_pj_list; + projCtx ctx = pj_ctx_alloc(); + + src_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); + dst_pj_list = (projPJ *) calloc(test_count,sizeof(projPJ)); + + if(!reinit_every_iteration) + { + for( i = 0; i < test_count; i++ ) + { + TestItem *test = test_list + i; + + src_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->src_def ); + dst_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->dst_def ); + } + } + +/* -------------------------------------------------------------------- */ +/* Perform tests - over and over. */ +/* -------------------------------------------------------------------- */ + + for( i_iter = 0; i_iter < repeat_count; i_iter++ ) + { + for( i = 0; i < test_count; i++ ) + { + TestItem *test = test_list + i; + double x, y, z; + int error; + + x = test->src_x; + y = test->src_y; + z = test->src_z; + + if( reinit_every_iteration ) + { + src_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->src_def ); + dst_pj_list[i] = custom_pj_init_plus_ctx( ctx, test->dst_def ); + + { + int skipTest = (src_pj_list[i] == NULL || dst_pj_list[i] == NULL); + + if ( skipTest != test->skip ) + fprintf( stderr, "Threaded projection initialization does not match unthreaded initialization\n" ); + + if (skipTest) + { + pj_free( src_pj_list[i] ); + pj_free( dst_pj_list[i] ); + continue; + } + } + } + + if ( test->skip ) + continue; + + error = pj_transform( src_pj_list[i], dst_pj_list[i], 1, 0, + &x, &y, &z ); + + + if( error != test->dst_error ) + { + fprintf( stderr, "Got error %d, expected %d\n", + error, test->dst_error ); + } + + if( x != test->dst_x || y != test->dst_y || z != test->dst_z ) + { + fprintf( stderr, + "Got %.15g,%.15g,%.15g\n" + "Expected %.15g,%.15g,%.15g\n" + "Diff %.15g,%.15g,%.15g\n", + x, y, z, + test->dst_x, test->dst_y, test->dst_z, + x-test->dst_x, y-test->dst_y, z-test->dst_z); + } + + if( reinit_every_iteration ) + { + pj_free( src_pj_list[i] ); + pj_free( dst_pj_list[i] ); + } + } + } + +/* -------------------------------------------------------------------- */ +/* Cleanup */ +/* -------------------------------------------------------------------- */ + if( !reinit_every_iteration ) + { + for( i = 0; i < test_count; i++ ) + { + pj_free( src_pj_list[i] ); + pj_free( dst_pj_list[i] ); + } + } + + free( src_pj_list ); + free( dst_pj_list ); + + pj_ctx_free( ctx ); + + printf( "%d iterations of the %d tests complete in thread X\n", + repeat_count, test_count ); + + active_thread_count--; +} + +#ifdef _WIN32 +/************************************************************************/ +/* WinTestThread() */ +/************************************************************************/ + +static DWORD WINAPI WinTestThread( LPVOID lpParameter ) + +{ + TestThread(); + + return 0; +} + +#else +/************************************************************************/ +/* PosixTestThread() */ +/************************************************************************/ + +static void *PosixTestThread( void *pData ) + +{ + (void)pData; + TestThread(); + return NULL; +} +#endif + +/************************************************************************/ +/* main() */ +/************************************************************************/ +#ifdef _WIN32 +static DWORD WINAPI do_main( LPVOID unused ) +#else +static int do_main(void) +#endif +{ +/* -------------------------------------------------------------------- */ +/* Our first pass is to establish the correct answers for all */ +/* the tests. */ +/* -------------------------------------------------------------------- */ + int i, test_count = sizeof(test_list) / sizeof(TestItem); + + for( i = 0; i < test_count; i++ ) + { + TestItem *test = test_list + i; + + projPJ src_pj, dst_pj; + + src_pj = custom_pj_init_plus_ctx( pj_get_default_ctx(), test->src_def ); + dst_pj = custom_pj_init_plus_ctx( pj_get_default_ctx(), test->dst_def ); + + if( src_pj == NULL ) + { + printf( "Unable to translate:\n%s\n", test->src_def ); + test->skip = 1; + pj_free (dst_pj); + continue; + } + + if( dst_pj == NULL ) + { + printf( "Unable to translate:\n%s\n", test->dst_def ); + test->skip = 1; + pj_free (src_pj); + continue; + } + + test->dst_x = test->src_x; + test->dst_y = test->src_y; + test->dst_z = test->src_z; + + test->dst_error = pj_transform( src_pj, dst_pj, 1, 0, + &(test->dst_x), + &(test->dst_y), + &(test->dst_z) ); + + pj_free( src_pj ); + pj_free( dst_pj ); + + test->skip = 0; + +#ifdef notdef + printf( "Test %d - output %.14g,%.14g,%g\n", i, test->dst_x, test->dst_y, test->dst_z ); +#endif + } + + printf( "%d tests initialized.\n", test_count ); + +/* -------------------------------------------------------------------- */ +/* Now launch a bunch of threads to repeat the tests. */ +/* -------------------------------------------------------------------- */ +#ifdef _WIN32 + + { //Scoped to workaround lack of c99 support in VS + HANDLE ahThread[num_threads]; + + for( i = 0; i < num_threads; i++ ) + { + active_thread_count++; + + ahThread[i] = CreateThread(NULL, 0, WinTestThread, NULL, 0, NULL); + + if (ahThread[i] == 0) + { + printf( "Thread creation failed."); + return 1; + } + } + + printf( "%d test threads launched.\n", num_threads ); + + WaitForMultipleObjects(num_threads, ahThread, TRUE, INFINITE); + } + +#else + { + pthread_t ahThread[num_threads]; + pthread_attr_t hThreadAttr; + + pthread_attr_init( &hThreadAttr ); + pthread_attr_setdetachstate( &hThreadAttr, PTHREAD_CREATE_DETACHED ); + + for( i = 0; i < num_threads; i++ ) + { + active_thread_count++; + + pthread_create( &(ahThread[i]), &hThreadAttr, + PosixTestThread, NULL ); + } + + printf( "%d test threads launched.\n", num_threads ); + + while( active_thread_count > 0 ) + sleep( 1 ); + } +#endif + + printf( "all tests complete.\n" ); + + return 0; +} + + +int main( int argc, char **argv ) +{ + int i; + for(i=0;i -#include - -#define PJ_LIB__ -#include "proj_internal.h" -#include "projects.h" -#define U_SEC_TO_RAD 4.848136811095359935899141023e-12 - -/************************************************************************/ -/* swap_words() */ -/* */ -/* Convert the byte order of the given word(s) in place. */ -/************************************************************************/ - -static const int byte_order_test = 1; -#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) - -static void swap_words( void *data_in, int word_size, int word_count ) - -{ - int word; - unsigned char *data = (unsigned char *) data_in; - - for( word = 0; word < word_count; word++ ) - { - int i; - - for( i = 0; i < word_size/2; i++ ) - { - unsigned char t; - - t = data[i]; - data[i] = data[word_size-i-1]; - data[word_size-i-1] = t; - } - - data += word_size; - } -} - -/************************************************************************/ -/* Usage() */ -/************************************************************************/ - -static void Usage() -{ - fprintf(stderr, - "usage: nad2bin [-f ctable/ctable2/ntv2] binary_output < ascii_source\n" ); - exit(1); -} - -/************************************************************************/ -/* main() */ -/************************************************************************/ -int main(int argc, char **argv) { - struct CTABLE ct; - FLP *p, t; - size_t tsize; - int i, j, ichk; - long lam, laml, phi, phil; - FILE *fp; - - const char *output_file = NULL; - - const char *format = "ctable2"; - const char *GS_TYPE = "SECONDS"; - const char *VERSION = ""; - const char *SYSTEM_F = "NAD27"; - const char *SYSTEM_T = "NAD83"; - const char *SUB_NAME = ""; - const char *CREATED = ""; - const char *UPDATED = ""; - -/* ==================================================================== */ -/* Process arguments. */ -/* ==================================================================== */ - for( i = 1; i < argc; i++ ) - { - if( i < argc-1 && strcmp(argv[i],"-f") == 0 ) - { - format = argv[++i]; - } - else if( output_file == NULL ) - { - output_file = argv[i]; - } - else - Usage(); - } - - if( output_file == NULL ) - Usage(); - - fprintf( stdout, "Output Binary File Format: %s\n", format ); - -/* ==================================================================== */ -/* Read the ASCII Table */ -/* ==================================================================== */ - - memset(ct.id,0,MAX_TAB_ID); - if ( NULL == fgets(ct.id, MAX_TAB_ID, stdin) ) { - perror("fgets"); - exit(1); - } - /* cppcheck-suppress invalidscanf */ - if ( EOF == scanf("%d %d %*d %lf %lf %lf %lf", &ct.lim.lam, &ct.lim.phi, - &ct.ll.lam, &ct.del.lam, &ct.ll.phi, &ct.del.phi) ) { - perror("scanf"); - exit(1); - } - if (!(ct.cvs = (FLP *)malloc(tsize = ct.lim.lam * ct.lim.phi * - sizeof(FLP)))) { - perror("mem. alloc"); - exit(1); - } - ct.ll.lam *= DEG_TO_RAD; - ct.ll.phi *= DEG_TO_RAD; - ct.del.lam *= DEG_TO_RAD; - ct.del.phi *= DEG_TO_RAD; - /* load table */ - p = ct.cvs; - for (i = 0; i < ct.lim.phi; ++i) { - /* cppcheck-suppress invalidscanf */ - if ( EOF == scanf("%d:%ld %ld", &ichk, &laml, &phil) ) { - perror("scanf on row"); - exit(1); - } - if (ichk != i) { - fprintf(stderr,"format check on row\n"); - exit(1); - } - t.lam = (float) (laml * U_SEC_TO_RAD); - t.phi = (float) (phil * U_SEC_TO_RAD); - *p++ = t; - for (j = 1; j < ct.lim.lam; ++j) { - /* cppcheck-suppress invalidscanf */ - if ( EOF == scanf("%ld %ld", &lam, &phi) ) { - perror("scanf on column"); - exit(1); - } - t.lam = (float) ((laml += lam) * U_SEC_TO_RAD); - t.phi = (float) ((phil += phi) * U_SEC_TO_RAD); - *p++ = t; - } - } - if (feof(stdin)) { - fprintf(stderr, "premature EOF\n"); - exit(1); - } - -/* ==================================================================== */ -/* Write out the old ctable format - this is machine and byte */ -/* order specific. */ -/* ==================================================================== */ - if( strcmp(format,"ctable") == 0 ) - { - if (!(fp = fopen(output_file, "wb"))) { - perror(output_file); - exit(2); - } - if (fwrite(&ct, sizeof(ct), 1, fp) != 1 || - fwrite(ct.cvs, tsize, 1, fp) != 1) { - fprintf(stderr, "output failure\n"); - exit(2); - } - fclose( fp ); - exit(0); /* normal completion */ - } - -/* ==================================================================== */ -/* Write out the old ctable format - this is machine and byte */ -/* order specific. */ -/* ==================================================================== */ - if( strcmp(format,"ctable2") == 0 ) - { - char header[160]; - - if (!(fp = fopen(output_file, "wb"))) { - perror(output_file); - exit(2); - } - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( MAX_TAB_ID == 80 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); /* for ct.lim.lam/phi */ - - memset( header, 0, sizeof(header) ); - - memcpy( header + 0, "CTABLE V2.0 ", 16 ); - memcpy( header + 16, ct.id, 80 ); - memcpy( header + 96, &ct.ll.lam, 8 ); - memcpy( header + 104, &ct.ll.phi, 8 ); - memcpy( header + 112, &ct.del.lam, 8 ); - memcpy( header + 120, &ct.del.phi, 8 ); - memcpy( header + 128, &ct.lim.lam, 4 ); - memcpy( header + 132, &ct.lim.phi, 4 ); - - /* force into LSB format */ - if( !IS_LSB ) - { - swap_words( header + 96, 8, 4 ); - swap_words( header + 128, 4, 2 ); - swap_words( ct.cvs, 4, ct.lim.lam * 2 * ct.lim.phi ); - } - - if( fwrite( header, sizeof(header), 1, fp ) != 1 ) { - perror( "fwrite" ); - exit( 2 ); - } - - if (fwrite(ct.cvs, tsize, 1, fp) != 1) { - perror( "fwrite" ); - exit(2); - } - - fclose( fp ); - exit(0); /* normal completion */ - } - -/* ==================================================================== */ -/* Write out the NTv2 format grid shift file. */ -/* ==================================================================== */ - if( strcmp(format,"ntv2") == 0 ) - { - if (!(fp = fopen(output_file, "wb"))) - { - perror(output_file); - exit(2); - } - -/* -------------------------------------------------------------------- */ -/* Write the file header. */ -/* -------------------------------------------------------------------- */ - { - char achHeader[11*16]; - - memset( achHeader, 0, sizeof(achHeader) ); - - memcpy( achHeader + 0*16, "NUM_OREC", 8 ); - achHeader[ 0*16 + 8] = 0xb; - - memcpy( achHeader + 1*16, "NUM_SREC", 8 ); - achHeader[ 1*16 + 8] = 0xb; - - memcpy( achHeader + 2*16, "NUM_FILE", 8 ); - achHeader[ 2*16 + 8] = 0x1; - - memcpy( achHeader + 3*16, "GS_TYPE ", 16 ); - memcpy( achHeader + 3*16+8, GS_TYPE, MIN(16,strlen(GS_TYPE)) ); - - memcpy( achHeader + 4*16, "VERSION ", 16 ); - memcpy( achHeader + 4*16+8, VERSION, MIN(16,strlen(VERSION)) ); - - memcpy( achHeader + 5*16, "SYSTEM_F ", 16 ); - memcpy( achHeader + 5*16+8, SYSTEM_F, MIN(16,strlen(SYSTEM_F)) ); - - memcpy( achHeader + 6*16, "SYSTEM_T ", 16 ); - memcpy( achHeader + 6*16+8, SYSTEM_T, MIN(16,strlen(SYSTEM_T)) ); - - memcpy( achHeader + 7*16, "MAJOR_F ", 8); - memcpy( achHeader + 8*16, "MINOR_F ", 8 ); - memcpy( achHeader + 9*16, "MAJOR_T ", 8 ); - memcpy( achHeader + 10*16, "MINOR_T ", 8 ); - - fwrite( achHeader, 1, sizeof(achHeader), fp ); - } - -/* -------------------------------------------------------------------- */ -/* Write the grid header. */ -/* -------------------------------------------------------------------- */ - { - unsigned char achHeader[11*16]; - double dfValue; - pj_int32 nGSCount = ct.lim.lam * ct.lim.phi; - LP ur; - - ur.lam = ct.ll.lam + (ct.lim.lam-1) * ct.del.lam; - ur.phi = ct.ll.phi + (ct.lim.phi-1) * ct.del.phi; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(nGSCount) == 4 ); - - memset( achHeader, 0, sizeof(achHeader) ); - - memcpy( achHeader + 0*16, "SUB_NAME ", 16 ); - memcpy( achHeader + 0*16+8, SUB_NAME, MIN(16,strlen(SUB_NAME)) ); - - memcpy( achHeader + 1*16, "PARENT ", 16 ); - memcpy( achHeader + 1*16+8, "NONE", MIN(16,strlen("NONE")) ); - - memcpy( achHeader + 2*16, "CREATED ", 16 ); - memcpy( achHeader + 2*16+8, CREATED, MIN(16,strlen(CREATED)) ); - - memcpy( achHeader + 3*16, "UPDATED ", 16 ); - memcpy( achHeader + 3*16+8, UPDATED, MIN(16,strlen(UPDATED)) ); - - memcpy( achHeader + 4*16, "S_LAT ", 8 ); - dfValue = ct.ll.phi * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 4*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 5*16, "N_LAT ", 8 ); - dfValue = ur.phi * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 5*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 6*16, "E_LONG ", 8 ); - dfValue = -1 * ur.lam * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 6*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 7*16, "W_LONG ", 8 ); - dfValue = -1 * ct.ll.lam * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 7*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 8*16, "LAT_INC ", 8 ); - dfValue = ct.del.phi * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 8*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 9*16, "LONG_INC", 8 ); - dfValue = ct.del.lam * 3600.0 / DEG_TO_RAD; - memcpy( achHeader + 9*16 + 8, &dfValue, 8 ); - - memcpy( achHeader + 10*16, "GS_COUNT", 8 ); - memcpy( achHeader + 10*16+8, &nGSCount, 4 ); - - if( !IS_LSB ) - { - swap_words( achHeader + 4*16 + 8, 8, 1 ); - swap_words( achHeader + 5*16 + 8, 8, 1 ); - swap_words( achHeader + 6*16 + 8, 8, 1 ); - swap_words( achHeader + 7*16 + 8, 8, 1 ); - swap_words( achHeader + 8*16 + 8, 8, 1 ); - swap_words( achHeader + 9*16 + 8, 8, 1 ); - swap_words( achHeader + 10*16 + 8, 4, 1 ); - } - - fwrite( achHeader, 1, sizeof(achHeader), fp ); - } - -/* -------------------------------------------------------------------- */ -/* Write the actual grid cells. */ -/* -------------------------------------------------------------------- */ - { - float *row_buf; - int row; - - row_buf = (float *) pj_malloc(ct.lim.lam * sizeof(float) * 4); - memset( row_buf, 0, sizeof(float)*4 ); - - for( row = 0; row < ct.lim.phi; row++ ) - { - for( i = 0; i < ct.lim.lam; i++ ) - { - FLP *cvs = ct.cvs + (row) * ct.lim.lam - + (ct.lim.lam - i - 1); - - /* convert radians to seconds */ - row_buf[i*4+0] = (float) (cvs->phi * (3600.0 / (M_PI/180.0))); - row_buf[i*4+1] = (float) (cvs->lam * (3600.0 / (M_PI/180.0))); - - /* We leave the accuracy values as zero */ - } - - if( !IS_LSB ) - swap_words( row_buf, 4, ct.lim.lam * 4 ); - - if( fwrite( row_buf, sizeof(float), ct.lim.lam*4, fp ) - != (size_t)( 4 * ct.lim.lam ) ) - { - perror( "write()" ); - exit( 2 ); - } - } - } - - fclose( fp ); - exit(0); /* normal completion */ - } - - fprintf( stderr, "Unsupported format, nothing written.\n" ); - exit( 3 ); -} diff --git a/src/nad2bin.cpp b/src/nad2bin.cpp new file mode 100644 index 00000000..eb8672a5 --- /dev/null +++ b/src/nad2bin.cpp @@ -0,0 +1,382 @@ +/* Convert bivariate ASCII NAD27 to NAD83 tables to NTv2 binary structure */ +#include +#include + +#define PJ_LIB__ +#include "proj_internal.h" +#include "projects.h" +#define U_SEC_TO_RAD 4.848136811095359935899141023e-12 + +/************************************************************************/ +/* swap_words() */ +/* */ +/* Convert the byte order of the given word(s) in place. */ +/************************************************************************/ + +static const int byte_order_test = 1; +#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) + +static void swap_words( void *data_in, int word_size, int word_count ) + +{ + int word; + unsigned char *data = (unsigned char *) data_in; + + for( word = 0; word < word_count; word++ ) + { + int i; + + for( i = 0; i < word_size/2; i++ ) + { + unsigned char t; + + t = data[i]; + data[i] = data[word_size-i-1]; + data[word_size-i-1] = t; + } + + data += word_size; + } +} + +/************************************************************************/ +/* Usage() */ +/************************************************************************/ + +static void Usage() +{ + fprintf(stderr, + "usage: nad2bin [-f ctable/ctable2/ntv2] binary_output < ascii_source\n" ); + exit(1); +} + +/************************************************************************/ +/* main() */ +/************************************************************************/ +int main(int argc, char **argv) { + struct CTABLE ct; + FLP *p, t; + size_t tsize; + int i, j, ichk; + long lam, laml, phi, phil; + FILE *fp; + + const char *output_file = NULL; + + const char *format = "ctable2"; + const char *GS_TYPE = "SECONDS"; + const char *VERSION = ""; + const char *SYSTEM_F = "NAD27"; + const char *SYSTEM_T = "NAD83"; + const char *SUB_NAME = ""; + const char *CREATED = ""; + const char *UPDATED = ""; + +/* ==================================================================== */ +/* Process arguments. */ +/* ==================================================================== */ + for( i = 1; i < argc; i++ ) + { + if( i < argc-1 && strcmp(argv[i],"-f") == 0 ) + { + format = argv[++i]; + } + else if( output_file == NULL ) + { + output_file = argv[i]; + } + else + Usage(); + } + + if( output_file == NULL ) + Usage(); + + fprintf( stdout, "Output Binary File Format: %s\n", format ); + +/* ==================================================================== */ +/* Read the ASCII Table */ +/* ==================================================================== */ + + memset(ct.id,0,MAX_TAB_ID); + if ( NULL == fgets(ct.id, MAX_TAB_ID, stdin) ) { + perror("fgets"); + exit(1); + } + /* cppcheck-suppress invalidscanf */ + if ( EOF == scanf("%d %d %*d %lf %lf %lf %lf", &ct.lim.lam, &ct.lim.phi, + &ct.ll.lam, &ct.del.lam, &ct.ll.phi, &ct.del.phi) ) { + perror("scanf"); + exit(1); + } + if (!(ct.cvs = (FLP *)malloc(tsize = ct.lim.lam * ct.lim.phi * + sizeof(FLP)))) { + perror("mem. alloc"); + exit(1); + } + ct.ll.lam *= DEG_TO_RAD; + ct.ll.phi *= DEG_TO_RAD; + ct.del.lam *= DEG_TO_RAD; + ct.del.phi *= DEG_TO_RAD; + /* load table */ + p = ct.cvs; + for (i = 0; i < ct.lim.phi; ++i) { + /* cppcheck-suppress invalidscanf */ + if ( EOF == scanf("%d:%ld %ld", &ichk, &laml, &phil) ) { + perror("scanf on row"); + exit(1); + } + if (ichk != i) { + fprintf(stderr,"format check on row\n"); + exit(1); + } + t.lam = (float) (laml * U_SEC_TO_RAD); + t.phi = (float) (phil * U_SEC_TO_RAD); + *p++ = t; + for (j = 1; j < ct.lim.lam; ++j) { + /* cppcheck-suppress invalidscanf */ + if ( EOF == scanf("%ld %ld", &lam, &phi) ) { + perror("scanf on column"); + exit(1); + } + t.lam = (float) ((laml += lam) * U_SEC_TO_RAD); + t.phi = (float) ((phil += phi) * U_SEC_TO_RAD); + *p++ = t; + } + } + if (feof(stdin)) { + fprintf(stderr, "premature EOF\n"); + exit(1); + } + +/* ==================================================================== */ +/* Write out the old ctable format - this is machine and byte */ +/* order specific. */ +/* ==================================================================== */ + if( strcmp(format,"ctable") == 0 ) + { + if (!(fp = fopen(output_file, "wb"))) { + perror(output_file); + exit(2); + } + if (fwrite(&ct, sizeof(ct), 1, fp) != 1 || + fwrite(ct.cvs, tsize, 1, fp) != 1) { + fprintf(stderr, "output failure\n"); + exit(2); + } + fclose( fp ); + exit(0); /* normal completion */ + } + +/* ==================================================================== */ +/* Write out the old ctable format - this is machine and byte */ +/* order specific. */ +/* ==================================================================== */ + if( strcmp(format,"ctable2") == 0 ) + { + char header[160]; + + if (!(fp = fopen(output_file, "wb"))) { + perror(output_file); + exit(2); + } + + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( MAX_TAB_ID == 80 ); + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(pj_int32) == 4 ); /* for ct.lim.lam/phi */ + + memset( header, 0, sizeof(header) ); + + memcpy( header + 0, "CTABLE V2.0 ", 16 ); + memcpy( header + 16, ct.id, 80 ); + memcpy( header + 96, &ct.ll.lam, 8 ); + memcpy( header + 104, &ct.ll.phi, 8 ); + memcpy( header + 112, &ct.del.lam, 8 ); + memcpy( header + 120, &ct.del.phi, 8 ); + memcpy( header + 128, &ct.lim.lam, 4 ); + memcpy( header + 132, &ct.lim.phi, 4 ); + + /* force into LSB format */ + if( !IS_LSB ) + { + swap_words( header + 96, 8, 4 ); + swap_words( header + 128, 4, 2 ); + swap_words( ct.cvs, 4, ct.lim.lam * 2 * ct.lim.phi ); + } + + if( fwrite( header, sizeof(header), 1, fp ) != 1 ) { + perror( "fwrite" ); + exit( 2 ); + } + + if (fwrite(ct.cvs, tsize, 1, fp) != 1) { + perror( "fwrite" ); + exit(2); + } + + fclose( fp ); + exit(0); /* normal completion */ + } + +/* ==================================================================== */ +/* Write out the NTv2 format grid shift file. */ +/* ==================================================================== */ + if( strcmp(format,"ntv2") == 0 ) + { + if (!(fp = fopen(output_file, "wb"))) + { + perror(output_file); + exit(2); + } + +/* -------------------------------------------------------------------- */ +/* Write the file header. */ +/* -------------------------------------------------------------------- */ + { + char achHeader[11*16]; + + memset( achHeader, 0, sizeof(achHeader) ); + + memcpy( achHeader + 0*16, "NUM_OREC", 8 ); + achHeader[ 0*16 + 8] = 0xb; + + memcpy( achHeader + 1*16, "NUM_SREC", 8 ); + achHeader[ 1*16 + 8] = 0xb; + + memcpy( achHeader + 2*16, "NUM_FILE", 8 ); + achHeader[ 2*16 + 8] = 0x1; + + memcpy( achHeader + 3*16, "GS_TYPE ", 16 ); + memcpy( achHeader + 3*16+8, GS_TYPE, MIN(16,strlen(GS_TYPE)) ); + + memcpy( achHeader + 4*16, "VERSION ", 16 ); + memcpy( achHeader + 4*16+8, VERSION, MIN(16,strlen(VERSION)) ); + + memcpy( achHeader + 5*16, "SYSTEM_F ", 16 ); + memcpy( achHeader + 5*16+8, SYSTEM_F, MIN(16,strlen(SYSTEM_F)) ); + + memcpy( achHeader + 6*16, "SYSTEM_T ", 16 ); + memcpy( achHeader + 6*16+8, SYSTEM_T, MIN(16,strlen(SYSTEM_T)) ); + + memcpy( achHeader + 7*16, "MAJOR_F ", 8); + memcpy( achHeader + 8*16, "MINOR_F ", 8 ); + memcpy( achHeader + 9*16, "MAJOR_T ", 8 ); + memcpy( achHeader + 10*16, "MINOR_T ", 8 ); + + fwrite( achHeader, 1, sizeof(achHeader), fp ); + } + +/* -------------------------------------------------------------------- */ +/* Write the grid header. */ +/* -------------------------------------------------------------------- */ + { + unsigned char achHeader[11*16]; + double dfValue; + pj_int32 nGSCount = ct.lim.lam * ct.lim.phi; + LP ur; + + ur.lam = ct.ll.lam + (ct.lim.lam-1) * ct.del.lam; + ur.phi = ct.ll.phi + (ct.lim.phi-1) * ct.del.phi; + + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(nGSCount) == 4 ); + + memset( achHeader, 0, sizeof(achHeader) ); + + memcpy( achHeader + 0*16, "SUB_NAME ", 16 ); + memcpy( achHeader + 0*16+8, SUB_NAME, MIN(16,strlen(SUB_NAME)) ); + + memcpy( achHeader + 1*16, "PARENT ", 16 ); + memcpy( achHeader + 1*16+8, "NONE", MIN(16,strlen("NONE")) ); + + memcpy( achHeader + 2*16, "CREATED ", 16 ); + memcpy( achHeader + 2*16+8, CREATED, MIN(16,strlen(CREATED)) ); + + memcpy( achHeader + 3*16, "UPDATED ", 16 ); + memcpy( achHeader + 3*16+8, UPDATED, MIN(16,strlen(UPDATED)) ); + + memcpy( achHeader + 4*16, "S_LAT ", 8 ); + dfValue = ct.ll.phi * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 4*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 5*16, "N_LAT ", 8 ); + dfValue = ur.phi * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 5*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 6*16, "E_LONG ", 8 ); + dfValue = -1 * ur.lam * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 6*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 7*16, "W_LONG ", 8 ); + dfValue = -1 * ct.ll.lam * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 7*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 8*16, "LAT_INC ", 8 ); + dfValue = ct.del.phi * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 8*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 9*16, "LONG_INC", 8 ); + dfValue = ct.del.lam * 3600.0 / DEG_TO_RAD; + memcpy( achHeader + 9*16 + 8, &dfValue, 8 ); + + memcpy( achHeader + 10*16, "GS_COUNT", 8 ); + memcpy( achHeader + 10*16+8, &nGSCount, 4 ); + + if( !IS_LSB ) + { + swap_words( achHeader + 4*16 + 8, 8, 1 ); + swap_words( achHeader + 5*16 + 8, 8, 1 ); + swap_words( achHeader + 6*16 + 8, 8, 1 ); + swap_words( achHeader + 7*16 + 8, 8, 1 ); + swap_words( achHeader + 8*16 + 8, 8, 1 ); + swap_words( achHeader + 9*16 + 8, 8, 1 ); + swap_words( achHeader + 10*16 + 8, 4, 1 ); + } + + fwrite( achHeader, 1, sizeof(achHeader), fp ); + } + +/* -------------------------------------------------------------------- */ +/* Write the actual grid cells. */ +/* -------------------------------------------------------------------- */ + { + float *row_buf; + int row; + + row_buf = (float *) pj_malloc(ct.lim.lam * sizeof(float) * 4); + memset( row_buf, 0, sizeof(float)*4 ); + + for( row = 0; row < ct.lim.phi; row++ ) + { + for( i = 0; i < ct.lim.lam; i++ ) + { + FLP *cvs = ct.cvs + (row) * ct.lim.lam + + (ct.lim.lam - i - 1); + + /* convert radians to seconds */ + row_buf[i*4+0] = (float) (cvs->phi * (3600.0 / (M_PI/180.0))); + row_buf[i*4+1] = (float) (cvs->lam * (3600.0 / (M_PI/180.0))); + + /* We leave the accuracy values as zero */ + } + + if( !IS_LSB ) + swap_words( row_buf, 4, ct.lim.lam * 4 ); + + if( fwrite( row_buf, sizeof(float), ct.lim.lam*4, fp ) + != (size_t)( 4 * ct.lim.lam ) ) + { + perror( "write()" ); + exit( 2 ); + } + } + } + + fclose( fp ); + exit(0); /* normal completion */ + } + + fprintf( stderr, "Unsupported format, nothing written.\n" ); + exit( 3 ); +} diff --git a/src/nad_cvt.c b/src/nad_cvt.c deleted file mode 100644 index ec4a2b47..00000000 --- a/src/nad_cvt.c +++ /dev/null @@ -1,76 +0,0 @@ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" -#include "proj_math.h" - -#define MAX_ITERATIONS 10 -#define TOL 1e-12 - -LP nad_cvt(LP in, int inverse, struct CTABLE *ct) { - LP t, tb,del, dif; - int i = MAX_ITERATIONS; - const double toltol = TOL*TOL; - - if (in.lam == HUGE_VAL) - return in; - - /* normalize input to ll origin */ - tb = in; - tb.lam -= ct->ll.lam; - tb.phi -= ct->ll.phi; - tb.lam = adjlon (tb.lam - M_PI) + M_PI; - - t = nad_intr (tb, ct); - if (t.lam == HUGE_VAL) - return t; - - if (!inverse) { - in.lam -= t.lam; - in.phi += t.phi; - return in; - } - - t.lam = tb.lam + t.lam; - t.phi = tb.phi - t.phi; - - do { - del = nad_intr(t, ct); - - /* This case used to return failure, but I have - changed it to return the first order approximation - of the inverse shift. This avoids cases where the - grid shift *into* this grid came from another grid. - While we aren't returning optimally correct results - I feel a close result in this case is better than - no result. NFW - To demonstrate use -112.5839956 49.4914451 against - the NTv2 grid shift file from Canada. */ - if (del.lam == HUGE_VAL) - break; - - dif.lam = t.lam - del.lam - tb.lam; - dif.phi = t.phi + del.phi - tb.phi; - t.lam -= dif.lam; - t.phi -= dif.phi; - - } while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); /* prob. slightly faster than hypot() */ - - if (i==0) { - /* If we had access to a context, this should go through pj_log, and we should set ctx->errno */ - if (getenv ("PROJ_DEBUG")) - fprintf( stderr, "Inverse grid shift iterator failed to converge.\n" ); - t.lam = t.phi = HUGE_VAL; - return t; - } - - /* and again: pj_log and ctx->errno */ - if (del.lam==HUGE_VAL && getenv ("PROJ_DEBUG")) - fprintf (stderr, "Inverse grid shift iteration failed, presumably at grid edge.\nUsing first approximation.\n"); - - in.lam = adjlon (t.lam + ct->ll.lam); - in.phi = t.phi + ct->ll.phi; - return in; -} diff --git a/src/nad_cvt.cpp b/src/nad_cvt.cpp new file mode 100644 index 00000000..ec4a2b47 --- /dev/null +++ b/src/nad_cvt.cpp @@ -0,0 +1,76 @@ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" +#include "proj_math.h" + +#define MAX_ITERATIONS 10 +#define TOL 1e-12 + +LP nad_cvt(LP in, int inverse, struct CTABLE *ct) { + LP t, tb,del, dif; + int i = MAX_ITERATIONS; + const double toltol = TOL*TOL; + + if (in.lam == HUGE_VAL) + return in; + + /* normalize input to ll origin */ + tb = in; + tb.lam -= ct->ll.lam; + tb.phi -= ct->ll.phi; + tb.lam = adjlon (tb.lam - M_PI) + M_PI; + + t = nad_intr (tb, ct); + if (t.lam == HUGE_VAL) + return t; + + if (!inverse) { + in.lam -= t.lam; + in.phi += t.phi; + return in; + } + + t.lam = tb.lam + t.lam; + t.phi = tb.phi - t.phi; + + do { + del = nad_intr(t, ct); + + /* This case used to return failure, but I have + changed it to return the first order approximation + of the inverse shift. This avoids cases where the + grid shift *into* this grid came from another grid. + While we aren't returning optimally correct results + I feel a close result in this case is better than + no result. NFW + To demonstrate use -112.5839956 49.4914451 against + the NTv2 grid shift file from Canada. */ + if (del.lam == HUGE_VAL) + break; + + dif.lam = t.lam - del.lam - tb.lam; + dif.phi = t.phi + del.phi - tb.phi; + t.lam -= dif.lam; + t.phi -= dif.phi; + + } while (--i && (dif.lam*dif.lam + dif.phi*dif.phi > toltol)); /* prob. slightly faster than hypot() */ + + if (i==0) { + /* If we had access to a context, this should go through pj_log, and we should set ctx->errno */ + if (getenv ("PROJ_DEBUG")) + fprintf( stderr, "Inverse grid shift iterator failed to converge.\n" ); + t.lam = t.phi = HUGE_VAL; + return t; + } + + /* and again: pj_log and ctx->errno */ + if (del.lam==HUGE_VAL && getenv ("PROJ_DEBUG")) + fprintf (stderr, "Inverse grid shift iteration failed, presumably at grid edge.\nUsing first approximation.\n"); + + in.lam = adjlon (t.lam + ct->ll.lam); + in.phi = t.phi + ct->ll.phi; + return in; +} diff --git a/src/nad_init.c b/src/nad_init.c deleted file mode 100644 index 8b024ac7..00000000 --- a/src/nad_init.c +++ /dev/null @@ -1,305 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Load datum shift files into memory. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include -#include - -#include "projects.h" - -/************************************************************************/ -/* swap_words() */ -/* */ -/* Convert the byte order of the given word(s) in place. */ -/************************************************************************/ - -static const int byte_order_test = 1; -#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) - -static void swap_words( void *data_in, int word_size, int word_count ) - -{ - int word; - unsigned char *data = (unsigned char *) data_in; - - for( word = 0; word < word_count; word++ ) - { - int i; - - for( i = 0; i < word_size/2; i++ ) - { - unsigned char t; - - t = data[i]; - data[i] = data[word_size-i-1]; - data[word_size-i-1] = t; - } - - data += word_size; - } -} - -/************************************************************************/ -/* nad_ctable_load() */ -/* */ -/* Load the data portion of a ctable formatted grid. */ -/************************************************************************/ - -int nad_ctable_load( projCtx ctx, struct CTABLE *ct, PAFile fid ) - -{ - size_t a_size; - - pj_ctx_fseek( ctx, fid, sizeof(struct CTABLE), SEEK_SET ); - - /* read all the actual shift values */ - a_size = ct->lim.lam * ct->lim.phi; - ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); - if( ct->cvs == NULL - || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) - { - pj_dalloc( ct->cvs ); - ct->cvs = NULL; - - pj_log( ctx, PJ_LOG_ERROR, - "ctable loading failed on fread() - binary incompatible?" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - return 1; -} - -/************************************************************************/ -/* nad_ctable_init() */ -/* */ -/* Read the header portion of a "ctable" format grid. */ -/************************************************************************/ - -struct CTABLE *nad_ctable_init( projCtx ctx, PAFile fid ) -{ - struct CTABLE *ct; - int id_end; - - /* read the table header */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if( ct == NULL - || pj_ctx_fread( ctx, ct, sizeof(struct CTABLE), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return NULL; - } - - /* do some minimal validation to ensure the structure isn't corrupt */ - if( ct->lim.lam < 1 || ct->lim.lam > 100000 - || ct->lim.phi < 1 || ct->lim.phi > 100000 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return NULL; - } - - /* trim white space and newlines off id */ - for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) - { - if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) - ct->id[id_end] = '\0'; - else - break; - } - - ct->cvs = NULL; - - return ct; -} - -/************************************************************************/ -/* nad_ctable2_load() */ -/* */ -/* Load the data portion of a ctable2 formatted grid. */ -/************************************************************************/ - -int nad_ctable2_load( projCtx ctx, struct CTABLE *ct, PAFile fid ) - -{ - size_t a_size; - - pj_ctx_fseek( ctx, fid, 160, SEEK_SET ); - - /* read all the actual shift values */ - a_size = ct->lim.lam * ct->lim.phi; - ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); - if( ct->cvs == NULL - || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) - { - pj_dalloc( ct->cvs ); - ct->cvs = NULL; - - if( getenv("PROJ_DEBUG") != NULL ) - { - fprintf( stderr, - "ctable2 loading failed on fread() - binary incompatible?\n" ); - } - - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( !IS_LSB ) - { - swap_words( ct->cvs, 4, (int)a_size * 2 ); - } - - return 1; -} - -/************************************************************************/ -/* nad_ctable2_init() */ -/* */ -/* Read the header portion of a "ctable2" format grid. */ -/************************************************************************/ - -struct CTABLE *nad_ctable2_init( projCtx ctx, PAFile fid ) -{ - struct CTABLE *ct; - int id_end; - char header[160]; - - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return NULL; - } - - if( !IS_LSB ) - { - swap_words( header + 96, 8, 4 ); - swap_words( header + 128, 4, 2 ); - } - - if( strncmp(header,"CTABLE V2",9) != 0 ) - { - pj_log( ctx, PJ_LOG_ERROR, "ctable2 - wrong header!" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return NULL; - } - - /* read the table header */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if( ct == NULL ) - { - pj_ctx_set_errno( ctx, ENOMEM ); - return NULL; - } - - memcpy( ct->id, header + 16, 80 ); - memcpy( &ct->ll.lam, header + 96, 8 ); - memcpy( &ct->ll.phi, header + 104, 8 ); - memcpy( &ct->del.lam, header + 112, 8 ); - memcpy( &ct->del.phi, header + 120, 8 ); - memcpy( &ct->lim.lam, header + 128, 4 ); - memcpy( &ct->lim.phi, header + 132, 4 ); - - /* do some minimal validation to ensure the structure isn't corrupt */ - if( ct->lim.lam < 1 || ct->lim.lam > 100000 - || ct->lim.phi < 1 || ct->lim.phi > 100000 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_dalloc( ct ); - return NULL; - } - - /* trim white space and newlines off id */ - for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) - { - if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) - ct->id[id_end] = '\0'; - else - break; - } - - ct->cvs = NULL; - - return ct; -} - -/************************************************************************/ -/* nad_init() */ -/* */ -/* Read a datum shift file in any of the supported binary formats. */ -/************************************************************************/ - -struct CTABLE *nad_init(projCtx ctx, char *name) -{ - char fname[MAX_PATH_FILENAME+1]; - struct CTABLE *ct; - PAFile fid; - - ctx->last_errno = 0; - -/* -------------------------------------------------------------------- */ -/* Open the file using the usual search rules. */ -/* -------------------------------------------------------------------- */ - strcpy(fname, name); - if (!(fid = pj_open_lib(ctx, fname, "rb"))) { - return 0; - } - - ct = nad_ctable_init( ctx, fid ); - if( ct != NULL ) - { - if( !nad_ctable_load( ctx, ct, fid ) ) - { - nad_free( ct ); - ct = NULL; - } - } - - pj_ctx_fclose(ctx, fid); - return ct; -} - -/************************************************************************/ -/* nad_free() */ -/* */ -/* Free a CTABLE grid shift structure produced by nad_init(). */ -/************************************************************************/ - -void nad_free(struct CTABLE *ct) -{ - if (ct) { - if( ct->cvs != NULL ) - pj_dalloc(ct->cvs); - - pj_dalloc(ct); - } -} diff --git a/src/nad_init.cpp b/src/nad_init.cpp new file mode 100644 index 00000000..8b024ac7 --- /dev/null +++ b/src/nad_init.cpp @@ -0,0 +1,305 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Load datum shift files into memory. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include +#include + +#include "projects.h" + +/************************************************************************/ +/* swap_words() */ +/* */ +/* Convert the byte order of the given word(s) in place. */ +/************************************************************************/ + +static const int byte_order_test = 1; +#define IS_LSB (((const unsigned char *) (&byte_order_test))[0] == 1) + +static void swap_words( void *data_in, int word_size, int word_count ) + +{ + int word; + unsigned char *data = (unsigned char *) data_in; + + for( word = 0; word < word_count; word++ ) + { + int i; + + for( i = 0; i < word_size/2; i++ ) + { + unsigned char t; + + t = data[i]; + data[i] = data[word_size-i-1]; + data[word_size-i-1] = t; + } + + data += word_size; + } +} + +/************************************************************************/ +/* nad_ctable_load() */ +/* */ +/* Load the data portion of a ctable formatted grid. */ +/************************************************************************/ + +int nad_ctable_load( projCtx ctx, struct CTABLE *ct, PAFile fid ) + +{ + size_t a_size; + + pj_ctx_fseek( ctx, fid, sizeof(struct CTABLE), SEEK_SET ); + + /* read all the actual shift values */ + a_size = ct->lim.lam * ct->lim.phi; + ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); + if( ct->cvs == NULL + || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) + { + pj_dalloc( ct->cvs ); + ct->cvs = NULL; + + pj_log( ctx, PJ_LOG_ERROR, + "ctable loading failed on fread() - binary incompatible?" ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + + return 1; +} + +/************************************************************************/ +/* nad_ctable_init() */ +/* */ +/* Read the header portion of a "ctable" format grid. */ +/************************************************************************/ + +struct CTABLE *nad_ctable_init( projCtx ctx, PAFile fid ) +{ + struct CTABLE *ct; + int id_end; + + /* read the table header */ + ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); + if( ct == NULL + || pj_ctx_fread( ctx, ct, sizeof(struct CTABLE), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_dalloc( ct ); + return NULL; + } + + /* do some minimal validation to ensure the structure isn't corrupt */ + if( ct->lim.lam < 1 || ct->lim.lam > 100000 + || ct->lim.phi < 1 || ct->lim.phi > 100000 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_dalloc( ct ); + return NULL; + } + + /* trim white space and newlines off id */ + for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) + { + if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) + ct->id[id_end] = '\0'; + else + break; + } + + ct->cvs = NULL; + + return ct; +} + +/************************************************************************/ +/* nad_ctable2_load() */ +/* */ +/* Load the data portion of a ctable2 formatted grid. */ +/************************************************************************/ + +int nad_ctable2_load( projCtx ctx, struct CTABLE *ct, PAFile fid ) + +{ + size_t a_size; + + pj_ctx_fseek( ctx, fid, 160, SEEK_SET ); + + /* read all the actual shift values */ + a_size = ct->lim.lam * ct->lim.phi; + ct->cvs = (FLP *) pj_malloc(sizeof(FLP) * a_size); + if( ct->cvs == NULL + || pj_ctx_fread(ctx, ct->cvs, sizeof(FLP), a_size, fid) != a_size ) + { + pj_dalloc( ct->cvs ); + ct->cvs = NULL; + + if( getenv("PROJ_DEBUG") != NULL ) + { + fprintf( stderr, + "ctable2 loading failed on fread() - binary incompatible?\n" ); + } + + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + + if( !IS_LSB ) + { + swap_words( ct->cvs, 4, (int)a_size * 2 ); + } + + return 1; +} + +/************************************************************************/ +/* nad_ctable2_init() */ +/* */ +/* Read the header portion of a "ctable2" format grid. */ +/************************************************************************/ + +struct CTABLE *nad_ctable2_init( projCtx ctx, PAFile fid ) +{ + struct CTABLE *ct; + int id_end; + char header[160]; + + if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return NULL; + } + + if( !IS_LSB ) + { + swap_words( header + 96, 8, 4 ); + swap_words( header + 128, 4, 2 ); + } + + if( strncmp(header,"CTABLE V2",9) != 0 ) + { + pj_log( ctx, PJ_LOG_ERROR, "ctable2 - wrong header!" ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return NULL; + } + + /* read the table header */ + ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); + if( ct == NULL ) + { + pj_ctx_set_errno( ctx, ENOMEM ); + return NULL; + } + + memcpy( ct->id, header + 16, 80 ); + memcpy( &ct->ll.lam, header + 96, 8 ); + memcpy( &ct->ll.phi, header + 104, 8 ); + memcpy( &ct->del.lam, header + 112, 8 ); + memcpy( &ct->del.phi, header + 120, 8 ); + memcpy( &ct->lim.lam, header + 128, 4 ); + memcpy( &ct->lim.phi, header + 132, 4 ); + + /* do some minimal validation to ensure the structure isn't corrupt */ + if( ct->lim.lam < 1 || ct->lim.lam > 100000 + || ct->lim.phi < 1 || ct->lim.phi > 100000 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_dalloc( ct ); + return NULL; + } + + /* trim white space and newlines off id */ + for( id_end = (int)strlen(ct->id)-1; id_end > 0; id_end-- ) + { + if( ct->id[id_end] == '\n' || ct->id[id_end] == ' ' ) + ct->id[id_end] = '\0'; + else + break; + } + + ct->cvs = NULL; + + return ct; +} + +/************************************************************************/ +/* nad_init() */ +/* */ +/* Read a datum shift file in any of the supported binary formats. */ +/************************************************************************/ + +struct CTABLE *nad_init(projCtx ctx, char *name) +{ + char fname[MAX_PATH_FILENAME+1]; + struct CTABLE *ct; + PAFile fid; + + ctx->last_errno = 0; + +/* -------------------------------------------------------------------- */ +/* Open the file using the usual search rules. */ +/* -------------------------------------------------------------------- */ + strcpy(fname, name); + if (!(fid = pj_open_lib(ctx, fname, "rb"))) { + return 0; + } + + ct = nad_ctable_init( ctx, fid ); + if( ct != NULL ) + { + if( !nad_ctable_load( ctx, ct, fid ) ) + { + nad_free( ct ); + ct = NULL; + } + } + + pj_ctx_fclose(ctx, fid); + return ct; +} + +/************************************************************************/ +/* nad_free() */ +/* */ +/* Free a CTABLE grid shift structure produced by nad_init(). */ +/************************************************************************/ + +void nad_free(struct CTABLE *ct) +{ + if (ct) { + if( ct->cvs != NULL ) + pj_dalloc(ct->cvs); + + pj_dalloc(ct); + } +} diff --git a/src/nad_intr.c b/src/nad_intr.c deleted file mode 100644 index 1f9d1e0c..00000000 --- a/src/nad_intr.c +++ /dev/null @@ -1,68 +0,0 @@ -/* Determine nad table correction value */ -#define PJ_LIB__ -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - - LP -nad_intr(LP t, struct CTABLE *ct) { - LP val, frct; - ILP indx; - double m00, m10, m01, m11; - FLP *f00, *f10, *f01, *f11; - long index; - int in; - - t.lam /= ct->del.lam; - indx.lam = isnan(t.lam) ? 0 : (pj_int32)lround(floor(t.lam)); - t.phi /= ct->del.phi; - indx.phi = isnan(t.phi) ? 0 : (pj_int32)lround(floor(t.phi)); - - frct.lam = t.lam - indx.lam; - frct.phi = t.phi - indx.phi; - val.lam = val.phi = HUGE_VAL; - if (indx.lam < 0) { - if (indx.lam == -1 && frct.lam > 0.99999999999) { - ++indx.lam; - frct.lam = 0.; - } else - return val; - } else if ((in = indx.lam + 1) >= ct->lim.lam) { - if (in == ct->lim.lam && frct.lam < 1e-11) { - --indx.lam; - frct.lam = 1.; - } else - return val; - } - if (indx.phi < 0) { - if (indx.phi == -1 && frct.phi > 0.99999999999) { - ++indx.phi; - frct.phi = 0.; - } else - return val; - } else if ((in = indx.phi + 1) >= ct->lim.phi) { - if (in == ct->lim.phi && frct.phi < 1e-11) { - --indx.phi; - frct.phi = 1.; - } else - return val; - } - index = indx.phi * ct->lim.lam + indx.lam; - f00 = ct->cvs + index++; - f10 = ct->cvs + index; - index += ct->lim.lam; - f11 = ct->cvs + index--; - f01 = ct->cvs + index; - m11 = m10 = frct.lam; - m00 = m01 = 1. - frct.lam; - m11 *= frct.phi; - m01 *= frct.phi; - frct.phi = 1. - frct.phi; - m00 *= frct.phi; - m10 *= frct.phi; - val.lam = m00 * f00->lam + m10 * f10->lam + - m01 * f01->lam + m11 * f11->lam; - val.phi = m00 * f00->phi + m10 * f10->phi + - m01 * f01->phi + m11 * f11->phi; - return val; -} diff --git a/src/nad_intr.cpp b/src/nad_intr.cpp new file mode 100644 index 00000000..1f9d1e0c --- /dev/null +++ b/src/nad_intr.cpp @@ -0,0 +1,68 @@ +/* Determine nad table correction value */ +#define PJ_LIB__ +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + + LP +nad_intr(LP t, struct CTABLE *ct) { + LP val, frct; + ILP indx; + double m00, m10, m01, m11; + FLP *f00, *f10, *f01, *f11; + long index; + int in; + + t.lam /= ct->del.lam; + indx.lam = isnan(t.lam) ? 0 : (pj_int32)lround(floor(t.lam)); + t.phi /= ct->del.phi; + indx.phi = isnan(t.phi) ? 0 : (pj_int32)lround(floor(t.phi)); + + frct.lam = t.lam - indx.lam; + frct.phi = t.phi - indx.phi; + val.lam = val.phi = HUGE_VAL; + if (indx.lam < 0) { + if (indx.lam == -1 && frct.lam > 0.99999999999) { + ++indx.lam; + frct.lam = 0.; + } else + return val; + } else if ((in = indx.lam + 1) >= ct->lim.lam) { + if (in == ct->lim.lam && frct.lam < 1e-11) { + --indx.lam; + frct.lam = 1.; + } else + return val; + } + if (indx.phi < 0) { + if (indx.phi == -1 && frct.phi > 0.99999999999) { + ++indx.phi; + frct.phi = 0.; + } else + return val; + } else if ((in = indx.phi + 1) >= ct->lim.phi) { + if (in == ct->lim.phi && frct.phi < 1e-11) { + --indx.phi; + frct.phi = 1.; + } else + return val; + } + index = indx.phi * ct->lim.lam + indx.lam; + f00 = ct->cvs + index++; + f10 = ct->cvs + index; + index += ct->lim.lam; + f11 = ct->cvs + index--; + f01 = ct->cvs + index; + m11 = m10 = frct.lam; + m00 = m01 = 1. - frct.lam; + m11 *= frct.phi; + m01 *= frct.phi; + frct.phi = 1. - frct.phi; + m00 *= frct.phi; + m10 *= frct.phi; + val.lam = m00 * f00->lam + m10 * f10->lam + + m01 * f01->lam + m11 * f11->lam; + val.phi = m00 * f00->phi + m10 * f10->phi + + m01 * f01->phi + m11 * f11->phi; + return val; +} diff --git a/src/p_series.c b/src/p_series.c deleted file mode 100644 index cddea888..00000000 --- a/src/p_series.c +++ /dev/null @@ -1,42 +0,0 @@ -/* print row coefficients of Tseries structure */ -#include "projects.h" -#include -#include -#define NF 20 /* length of final format string */ -#define CUT 60 /* check length of line */ - -/* FIXME: put the declaration in a header. Also used in gen_cheb.c */ -void p_series(Tseries *T, FILE *file, char *fmt); - -void p_series(Tseries *T, FILE *file, char *fmt) { - int i, j, n, L; - char format[NF+1]; - - *format = ' '; - strncpy(format + 1, fmt, NF - 3); - strcat(format, "%n"); - fprintf(file, "u: %d\n", T->mu+1); - for (i = 0; i <= T->mu; ++i) - if (T->cu[i].m) { - fprintf(file, "%d %d%n", i, T->cu[i].m, &L); - n = 0; - for (j = 0; j < T->cu[i].m; ++j) { - if ((L += n) > CUT) - fprintf(file, "\n %n", &L); - fprintf(file, format, T->cu[i].c[j], &n); - } - fputc('\n', file); - } - fprintf(file, "v: %d\n", T->mv+1); - for (i = 0; i <= T->mv; ++i) - if (T->cv[i].m) { - fprintf(file, "%d %d%n", i, T->cv[i].m, &L); - n = 0; - for (j = 0; j < T->cv[i].m; ++j) { - if ((L += n) > 60) - fprintf(file, "\n %n", &L); - fprintf(file, format, T->cv[i].c[j], &n); - } - fputc('\n', file); - } -} diff --git a/src/p_series.cpp b/src/p_series.cpp new file mode 100644 index 00000000..cddea888 --- /dev/null +++ b/src/p_series.cpp @@ -0,0 +1,42 @@ +/* print row coefficients of Tseries structure */ +#include "projects.h" +#include +#include +#define NF 20 /* length of final format string */ +#define CUT 60 /* check length of line */ + +/* FIXME: put the declaration in a header. Also used in gen_cheb.c */ +void p_series(Tseries *T, FILE *file, char *fmt); + +void p_series(Tseries *T, FILE *file, char *fmt) { + int i, j, n, L; + char format[NF+1]; + + *format = ' '; + strncpy(format + 1, fmt, NF - 3); + strcat(format, "%n"); + fprintf(file, "u: %d\n", T->mu+1); + for (i = 0; i <= T->mu; ++i) + if (T->cu[i].m) { + fprintf(file, "%d %d%n", i, T->cu[i].m, &L); + n = 0; + for (j = 0; j < T->cu[i].m; ++j) { + if ((L += n) > CUT) + fprintf(file, "\n %n", &L); + fprintf(file, format, T->cu[i].c[j], &n); + } + fputc('\n', file); + } + fprintf(file, "v: %d\n", T->mv+1); + for (i = 0; i <= T->mv; ++i) + if (T->cv[i].m) { + fprintf(file, "%d %d%n", i, T->cv[i].m, &L); + n = 0; + for (j = 0; j < T->cv[i].m; ++j) { + if ((L += n) > 60) + fprintf(file, "\n %n", &L); + fprintf(file, format, T->cv[i].c[j], &n); + } + fputc('\n', file); + } +} diff --git a/src/pj_apply_gridshift.c b/src/pj_apply_gridshift.c deleted file mode 100644 index 45ce5c8e..00000000 --- a/src/pj_apply_gridshift.c +++ /dev/null @@ -1,356 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Apply datum shifts based on grid shift files (normally NAD27 to - * NAD83 or the reverse). This module is responsible for keeping - * a list of loaded grids, and calling with each one that is - * allowed for a given datum (expressed as the nadgrids= parameter). - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -/************************************************************************/ -/* pj_apply_gridshift() */ -/* */ -/* This is the externally callable interface - part of the */ -/* public API - though it is not used internally any more and I */ -/* doubt it is used by any other applications. But we preserve */ -/* it to honour our public api. */ -/************************************************************************/ - -int pj_apply_gridshift( projCtx ctx, const char *nadgrids, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - PJ_GRIDINFO **gridlist; - int grid_count; - int ret; - - gridlist = pj_gridlist_from_nadgrids( ctx, nadgrids, &grid_count ); - - if( gridlist == NULL || grid_count == 0 ) - return ctx->last_errno; - - ret = pj_apply_gridshift_3( ctx, gridlist, grid_count, inverse, - point_count, point_offset, x, y, z ); - - /* - ** Note this frees the array of grid list pointers, but not the grids - ** which is as intended. The grids themselves live on. - */ - pj_dalloc( gridlist ); - - return ret; -} - -/************************************************************************/ -/* pj_apply_gridshift_2() */ -/* */ -/* This implementation uses the gridlist from a coordinate */ -/* system definition. If the gridlist has not yet been */ -/* populated in the coordinate system definition we set it up */ -/* now. */ -/************************************************************************/ - -int pj_apply_gridshift_2( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - if( defn->catalog_name != NULL ) - return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset, - x, y, z ); - - if( defn->gridlist == NULL ) - { - defn->gridlist = - pj_gridlist_from_nadgrids( pj_get_ctx( defn ), - pj_param(defn->ctx, defn->params,"snadgrids").s, - &(defn->gridlist_count) ); - - if( defn->gridlist == NULL || defn->gridlist_count == 0 ) - return defn->ctx->last_errno; - } - - return pj_apply_gridshift_3( pj_get_ctx( defn ), - defn->gridlist, defn->gridlist_count, inverse, - point_count, point_offset, x, y, z ); -} - -/************************************************************************/ -/* find_ctable() */ -/* */ -/* Determine which grid is the correct given an input coordinate. */ -/************************************************************************/ - -static struct CTABLE* find_ctable(projCtx ctx, LP input, int grid_count, PJ_GRIDINFO **tables) { - int itable; - - /* keep trying till we find a table that works */ - for( itable = 0; itable < grid_count; itable++ ) - { - - PJ_GRIDINFO *gi = tables[itable]; - struct CTABLE *ct = gi->ct; - double epsilon = (fabs(ct->del.phi)+fabs(ct->del.lam))/10000.0; - /* skip tables that don't match our point at all. */ - if ( ct->ll.phi - epsilon > input.phi - || ct->ll.lam - epsilon > input.lam - || (ct->ll.phi + (ct->lim.phi-1) * ct->del.phi + epsilon < input.phi) - || (ct->ll.lam + (ct->lim.lam-1) * ct->del.lam + epsilon < input.lam) ) { - continue; - } - - /* If we have child nodes, check to see if any of them apply. */ - while( gi->child ) - { - PJ_GRIDINFO *child; - - for( child = gi->child; child != NULL; child = child->next ) - { - struct CTABLE *ct1 = child->ct; - epsilon = (fabs(ct1->del.phi)+fabs(ct1->del.lam))/10000.0; - - if( ct1->ll.phi - epsilon > input.phi - || ct1->ll.lam - epsilon > input.lam - || (ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi + epsilon < input.phi) - || (ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam + epsilon < input.lam) ) { - continue; - } - break; - } - - /* If we didn't find a child then nothing more to do */ - if( child == NULL ) break; - - /* Otherwise use the child, first checking it's children */ - gi = child; - ct = child->ct; - } - /* load the grid shift info if we don't have it. */ - if( ct->cvs == NULL) { - if (!pj_gridinfo_load( ctx, gi ) ) { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return NULL; - } - } - /* if we get this far we have found a suitable grid */ - return ct; - } - - return NULL; -} - -/************************************************************************/ -/* pj_apply_gridshift_3() */ -/* */ -/* This is the real workhorse, given a gridlist. */ -/************************************************************************/ - -int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_count, - int inverse, long point_count, int point_offset, - double *x, double *y, double *z ) -{ - int i; - struct CTABLE *ct; - static int debug_count = 0; - (void) z; - - if( gridlist== NULL || gridlist_count == 0 ) - { - pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - LP input, output; - int itable; - - input.phi = y[io]; - input.lam = x[io]; - output.phi = HUGE_VAL; - output.lam = HUGE_VAL; - - ct = find_ctable(ctx, input, gridlist_count, gridlist); - if( ct != NULL ) - { - output = nad_cvt( input, inverse, ct ); - - if ( output.lam != HUGE_VAL && debug_count++ < 20 ) - pj_log( ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id ); - } - - if ( output.lam == HUGE_VAL ) - { - if( ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - for( itable = 0; itable < gridlist_count; itable++ ) - { - PJ_GRIDINFO *gi = gridlist[itable]; - if( itable == 0 ) - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, " tried: %s", gi->gridname ); - else - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, ",%s", gi->gridname ); - } - } - - /* - * We don't actually have any machinery currently to set the - * following macro, so this is mostly kept here to make it clear - * how we ought to operate if we wanted to make it super clear - * that an error has occurred when points are outside our available - * datum shift areas. But if this is on, we will find that "low - * value" points on the fringes of some datasets will completely - * fail causing lots of problems when it is more or less ok to - * just not apply a datum shift. So rather than deal with - * that we just fallback to no shift. (see also bug #45). - */ -#ifdef ERR_GRID_AREA_TRANSIENT_SEVERE - y[io] = HUGE_VAL; - x[io] = HUGE_VAL; -#else - /* leave x/y unshifted. */ -#endif - } - else - { - y[io] = output.phi; - x[io] = output.lam; - } - } - - return 0; -} - -/**********************************************/ -int proj_hgrid_init(PJ* P, const char *grids) { -/********************************************** - - Initizalize and populate list of horizontal - grids. - - Takes a PJ-object and the plus-parameter - name that is used in the proj-string to - specify the grids to load, e.g. "+grids". - The + should be left out here. - - Returns the number of loaded grids. - -***********************************************/ - - /* prepend "s" to the "grids" string to allow usage with pj_param */ - char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); - sprintf(sgrids, "%s%s", "s", grids); - - if (P->gridlist == NULL) { - P->gridlist = pj_gridlist_from_nadgrids( - P->ctx, - pj_param(P->ctx, P->params, sgrids).s, - &(P->gridlist_count) - ); - - if( P->gridlist == NULL || P->gridlist_count == 0 ) { - pj_dealloc(sgrids); - return 0; - } - } - - if (P->gridlist_count == 0) { - proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - pj_dealloc(sgrids); - return P->gridlist_count; -} - -/********************************************/ -/* proj_hgrid_value() */ -/* */ -/* Return coordinate offset in grid */ -/********************************************/ -LP proj_hgrid_value(PJ *P, LP lp) { - struct CTABLE *ct; - LP out = proj_coord_error().lp; - - ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); - if (ct == 0) { - pj_ctx_set_errno( P->ctx, PJD_ERR_GRID_AREA); - return out; - } - - /* normalize input to ll origin */ - lp.lam -= ct->ll.lam; - lp.phi -= ct->ll.phi; - - lp.lam = adjlon(lp.lam - M_PI) + M_PI; - - out = nad_intr(lp, ct); - - if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) { - pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); - } - - return out; -} - -LP proj_hgrid_apply(PJ *P, LP lp, PJ_DIRECTION direction) { - struct CTABLE *ct; - int inverse; - LP out; - - out.lam = HUGE_VAL; out.phi = HUGE_VAL; - - ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); - - if (ct == NULL || ct->cvs == NULL) { - pj_ctx_set_errno( P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return out; - } - - inverse = direction == PJ_FWD ? 0 : 1; - out = nad_cvt(lp, inverse, ct); - - if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) - pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); - - return out; - -} diff --git a/src/pj_apply_gridshift.cpp b/src/pj_apply_gridshift.cpp new file mode 100644 index 00000000..45ce5c8e --- /dev/null +++ b/src/pj_apply_gridshift.cpp @@ -0,0 +1,356 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Apply datum shifts based on grid shift files (normally NAD27 to + * NAD83 or the reverse). This module is responsible for keeping + * a list of loaded grids, and calling with each one that is + * allowed for a given datum (expressed as the nadgrids= parameter). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +/************************************************************************/ +/* pj_apply_gridshift() */ +/* */ +/* This is the externally callable interface - part of the */ +/* public API - though it is not used internally any more and I */ +/* doubt it is used by any other applications. But we preserve */ +/* it to honour our public api. */ +/************************************************************************/ + +int pj_apply_gridshift( projCtx ctx, const char *nadgrids, int inverse, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + PJ_GRIDINFO **gridlist; + int grid_count; + int ret; + + gridlist = pj_gridlist_from_nadgrids( ctx, nadgrids, &grid_count ); + + if( gridlist == NULL || grid_count == 0 ) + return ctx->last_errno; + + ret = pj_apply_gridshift_3( ctx, gridlist, grid_count, inverse, + point_count, point_offset, x, y, z ); + + /* + ** Note this frees the array of grid list pointers, but not the grids + ** which is as intended. The grids themselves live on. + */ + pj_dalloc( gridlist ); + + return ret; +} + +/************************************************************************/ +/* pj_apply_gridshift_2() */ +/* */ +/* This implementation uses the gridlist from a coordinate */ +/* system definition. If the gridlist has not yet been */ +/* populated in the coordinate system definition we set it up */ +/* now. */ +/************************************************************************/ + +int pj_apply_gridshift_2( PJ *defn, int inverse, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + if( defn->catalog_name != NULL ) + return pj_gc_apply_gridshift( defn, inverse, point_count, point_offset, + x, y, z ); + + if( defn->gridlist == NULL ) + { + defn->gridlist = + pj_gridlist_from_nadgrids( pj_get_ctx( defn ), + pj_param(defn->ctx, defn->params,"snadgrids").s, + &(defn->gridlist_count) ); + + if( defn->gridlist == NULL || defn->gridlist_count == 0 ) + return defn->ctx->last_errno; + } + + return pj_apply_gridshift_3( pj_get_ctx( defn ), + defn->gridlist, defn->gridlist_count, inverse, + point_count, point_offset, x, y, z ); +} + +/************************************************************************/ +/* find_ctable() */ +/* */ +/* Determine which grid is the correct given an input coordinate. */ +/************************************************************************/ + +static struct CTABLE* find_ctable(projCtx ctx, LP input, int grid_count, PJ_GRIDINFO **tables) { + int itable; + + /* keep trying till we find a table that works */ + for( itable = 0; itable < grid_count; itable++ ) + { + + PJ_GRIDINFO *gi = tables[itable]; + struct CTABLE *ct = gi->ct; + double epsilon = (fabs(ct->del.phi)+fabs(ct->del.lam))/10000.0; + /* skip tables that don't match our point at all. */ + if ( ct->ll.phi - epsilon > input.phi + || ct->ll.lam - epsilon > input.lam + || (ct->ll.phi + (ct->lim.phi-1) * ct->del.phi + epsilon < input.phi) + || (ct->ll.lam + (ct->lim.lam-1) * ct->del.lam + epsilon < input.lam) ) { + continue; + } + + /* If we have child nodes, check to see if any of them apply. */ + while( gi->child ) + { + PJ_GRIDINFO *child; + + for( child = gi->child; child != NULL; child = child->next ) + { + struct CTABLE *ct1 = child->ct; + epsilon = (fabs(ct1->del.phi)+fabs(ct1->del.lam))/10000.0; + + if( ct1->ll.phi - epsilon > input.phi + || ct1->ll.lam - epsilon > input.lam + || (ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi + epsilon < input.phi) + || (ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam + epsilon < input.lam) ) { + continue; + } + break; + } + + /* If we didn't find a child then nothing more to do */ + if( child == NULL ) break; + + /* Otherwise use the child, first checking it's children */ + gi = child; + ct = child->ct; + } + /* load the grid shift info if we don't have it. */ + if( ct->cvs == NULL) { + if (!pj_gridinfo_load( ctx, gi ) ) { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return NULL; + } + } + /* if we get this far we have found a suitable grid */ + return ct; + } + + return NULL; +} + +/************************************************************************/ +/* pj_apply_gridshift_3() */ +/* */ +/* This is the real workhorse, given a gridlist. */ +/************************************************************************/ + +int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_count, + int inverse, long point_count, int point_offset, + double *x, double *y, double *z ) +{ + int i; + struct CTABLE *ct; + static int debug_count = 0; + (void) z; + + if( gridlist== NULL || gridlist_count == 0 ) + { + pj_ctx_set_errno(ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + + ctx->last_errno = 0; + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + LP input, output; + int itable; + + input.phi = y[io]; + input.lam = x[io]; + output.phi = HUGE_VAL; + output.lam = HUGE_VAL; + + ct = find_ctable(ctx, input, gridlist_count, gridlist); + if( ct != NULL ) + { + output = nad_cvt( input, inverse, ct ); + + if ( output.lam != HUGE_VAL && debug_count++ < 20 ) + pj_log( ctx, PJ_LOG_DEBUG_MINOR, "pj_apply_gridshift(): used %s", ct->id ); + } + + if ( output.lam == HUGE_VAL ) + { + if( ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "pj_apply_gridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + for( itable = 0; itable < gridlist_count; itable++ ) + { + PJ_GRIDINFO *gi = gridlist[itable]; + if( itable == 0 ) + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, " tried: %s", gi->gridname ); + else + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, ",%s", gi->gridname ); + } + } + + /* + * We don't actually have any machinery currently to set the + * following macro, so this is mostly kept here to make it clear + * how we ought to operate if we wanted to make it super clear + * that an error has occurred when points are outside our available + * datum shift areas. But if this is on, we will find that "low + * value" points on the fringes of some datasets will completely + * fail causing lots of problems when it is more or less ok to + * just not apply a datum shift. So rather than deal with + * that we just fallback to no shift. (see also bug #45). + */ +#ifdef ERR_GRID_AREA_TRANSIENT_SEVERE + y[io] = HUGE_VAL; + x[io] = HUGE_VAL; +#else + /* leave x/y unshifted. */ +#endif + } + else + { + y[io] = output.phi; + x[io] = output.lam; + } + } + + return 0; +} + +/**********************************************/ +int proj_hgrid_init(PJ* P, const char *grids) { +/********************************************** + + Initizalize and populate list of horizontal + grids. + + Takes a PJ-object and the plus-parameter + name that is used in the proj-string to + specify the grids to load, e.g. "+grids". + The + should be left out here. + + Returns the number of loaded grids. + +***********************************************/ + + /* prepend "s" to the "grids" string to allow usage with pj_param */ + char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); + sprintf(sgrids, "%s%s", "s", grids); + + if (P->gridlist == NULL) { + P->gridlist = pj_gridlist_from_nadgrids( + P->ctx, + pj_param(P->ctx, P->params, sgrids).s, + &(P->gridlist_count) + ); + + if( P->gridlist == NULL || P->gridlist_count == 0 ) { + pj_dealloc(sgrids); + return 0; + } + } + + if (P->gridlist_count == 0) { + proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + pj_dealloc(sgrids); + return P->gridlist_count; +} + +/********************************************/ +/* proj_hgrid_value() */ +/* */ +/* Return coordinate offset in grid */ +/********************************************/ +LP proj_hgrid_value(PJ *P, LP lp) { + struct CTABLE *ct; + LP out = proj_coord_error().lp; + + ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); + if (ct == 0) { + pj_ctx_set_errno( P->ctx, PJD_ERR_GRID_AREA); + return out; + } + + /* normalize input to ll origin */ + lp.lam -= ct->ll.lam; + lp.phi -= ct->ll.phi; + + lp.lam = adjlon(lp.lam - M_PI) + M_PI; + + out = nad_intr(lp, ct); + + if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) { + pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); + } + + return out; +} + +LP proj_hgrid_apply(PJ *P, LP lp, PJ_DIRECTION direction) { + struct CTABLE *ct; + int inverse; + LP out; + + out.lam = HUGE_VAL; out.phi = HUGE_VAL; + + ct = find_ctable(P->ctx, lp, P->gridlist_count, P->gridlist); + + if (ct == NULL || ct->cvs == NULL) { + pj_ctx_set_errno( P->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return out; + } + + inverse = direction == PJ_FWD ? 0 : 1; + out = nad_cvt(lp, inverse, ct); + + if (out.lam == HUGE_VAL || out.phi == HUGE_VAL) + pj_ctx_set_errno(P->ctx, PJD_ERR_GRID_AREA); + + return out; + +} diff --git a/src/pj_apply_vgridshift.c b/src/pj_apply_vgridshift.c deleted file mode 100644 index c1344951..00000000 --- a/src/pj_apply_vgridshift.c +++ /dev/null @@ -1,331 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Apply vertical datum shifts based on grid shift files, normally - * geoid grids mapping WGS84 to NAVD88 or something similar. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2010, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "proj_math.h" -#include "proj_internal.h" -#include "projects.h" - -static int is_nodata(float value) -{ - /* nodata? */ - /* GTX official nodata value if -88.88880f, but some grids also */ - /* use other big values for nodata (e.g naptrans2008.gtx has */ - /* nodata values like -2147479936), so test them too */ - return value > 1000 || value < -1000 || value == -88.88880f; -} - -static double read_vgrid_value( PJ *defn, LP input, int *gridlist_count_p, PJ_GRIDINFO **tables, struct CTABLE *ct) { - int itable = 0; - double value = HUGE_VAL; - double grid_x, grid_y; - long grid_ix, grid_iy; - long grid_ix2, grid_iy2; - float *cvs; - /* do not deal with NaN coordinates */ - /* cppcheck-suppress duplicateExpression */ - if( isnan(input.phi) || isnan(input.lam) ) - itable = *gridlist_count_p; - - /* keep trying till we find a table that works */ - for ( ; itable < *gridlist_count_p; itable++ ) - { - PJ_GRIDINFO *gi = tables[itable]; - - ct = gi->ct; - - /* skip tables that don't match our point at all. */ - if( ct->ll.phi > input.phi || ct->ll.lam > input.lam - || ct->ll.phi + (ct->lim.phi-1) * ct->del.phi < input.phi - || ct->ll.lam + (ct->lim.lam-1) * ct->del.lam < input.lam ) - continue; - - /* If we have child nodes, check to see if any of them apply. */ - while( gi->child != NULL ) - { - PJ_GRIDINFO *child; - - for( child = gi->child; child != NULL; child = child->next ) - { - struct CTABLE *ct1 = child->ct; - - if( ct1->ll.phi > input.phi || ct1->ll.lam > input.lam - || ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi < input.phi - || ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam < input.lam) - continue; - - break; - } - - /* we didn't find a more refined child node to use, so go with current grid */ - if( child == NULL ) - { - break; - } - - /* Otherwise let's try for childrens children .. */ - gi = child; - ct = child->ct; - } - - /* load the grid shift info if we don't have it. */ - if( ct->cvs == NULL && !pj_gridinfo_load( pj_get_ctx(defn), gi ) ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - - /* Interpolation a location within the grid */ - grid_x = (input.lam - ct->ll.lam) / ct->del.lam; - grid_y = (input.phi - ct->ll.phi) / ct->del.phi; - grid_ix = lround(floor(grid_x)); - grid_iy = lround(floor(grid_y)); - grid_x -= grid_ix; - grid_y -= grid_iy; - - grid_ix2 = grid_ix + 1; - if( grid_ix2 >= ct->lim.lam ) - grid_ix2 = ct->lim.lam - 1; - grid_iy2 = grid_iy + 1; - if( grid_iy2 >= ct->lim.phi ) - grid_iy2 = ct->lim.phi - 1; - - cvs = (float *) ct->cvs; - { - float value_a = cvs[grid_ix + grid_iy * ct->lim.lam]; - float value_b = cvs[grid_ix2 + grid_iy * ct->lim.lam]; - float value_c = cvs[grid_ix + grid_iy2 * ct->lim.lam]; - float value_d = cvs[grid_ix2 + grid_iy2 * ct->lim.lam]; - double total_weight = 0.0; - int n_weights = 0; - value = 0.0f; - if( !is_nodata(value_a) ) - { - double weight = (1.0-grid_x) * (1.0-grid_y); - value += value_a * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_b) ) - { - double weight = (grid_x) * (1.0-grid_y); - value += value_b * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_c) ) - { - double weight = (1.0-grid_x) * (grid_y); - value += value_c * weight; - total_weight += weight; - n_weights ++; - } - if( !is_nodata(value_d) ) - { - double weight = (grid_x) * (grid_y); - value += value_d * weight; - total_weight += weight; - n_weights ++; - } - if( n_weights == 0 ) - value = HUGE_VAL; - else if( n_weights != 4 ) - value /= total_weight; - } - - } - - return value; -} - -/************************************************************************/ -/* pj_apply_vgridshift() */ -/* */ -/* This implementation takes uses the gridlist from a coordinate */ -/* system definition. If the gridlist has not yet been */ -/* populated in the coordinate system definition we set it up */ -/* now. */ -/************************************************************************/ -int pj_apply_vgridshift( PJ *defn, const char *listname, - PJ_GRIDINFO ***gridlist_p, - int *gridlist_count_p, - int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - static int debug_count = 0; - PJ_GRIDINFO **tables; - struct CTABLE ct; - - if( *gridlist_p == NULL ) - { - *gridlist_p = - pj_gridlist_from_nadgrids( pj_get_ctx(defn), - pj_param(defn->ctx,defn->params,listname).s, - gridlist_count_p ); - - if( *gridlist_p == NULL || *gridlist_count_p == 0 ) - return defn->ctx->last_errno; - } - - if( *gridlist_count_p == 0 ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - tables = *gridlist_p; - defn->ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) - { - double value; - long io = i * point_offset; - LP input; - - input.phi = y[io]; - input.lam = x[io]; - - value = read_vgrid_value(defn, input, gridlist_count_p, tables, &ct); - - if( inverse ) - z[io] -= value; - else - z[io] += value; - if( value != HUGE_VAL ) - { - if( debug_count++ < 20 ) { - proj_log_trace(defn, "pj_apply_gridshift(): used %s", ct.id); - break; - } - } - - if( value == HUGE_VAL ) - { - int itable; - char gridlist[3000]; - - proj_log_debug(defn, - "pj_apply_vgridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - - gridlist[0] = '\0'; - for( itable = 0; itable < *gridlist_count_p; itable++ ) - { - PJ_GRIDINFO *gi = tables[itable]; - if( strlen(gridlist) + strlen(gi->gridname) > sizeof(gridlist)-100 ) - { - strcat( gridlist, "..." ); - break; - } - - if( itable == 0 ) - sprintf( gridlist, " tried: %s", gi->gridname ); - else - sprintf( gridlist+strlen(gridlist), ",%s", gi->gridname ); - } - - proj_log_debug(defn, "%s", gridlist); - pj_ctx_set_errno( defn->ctx, PJD_ERR_GRID_AREA ); - - return PJD_ERR_GRID_AREA; - } - } - - return 0; -} - -/**********************************************/ -int proj_vgrid_init(PJ* P, const char *grids) { -/********************************************** - - Initizalize and populate gridlist. - - Takes a PJ-object and the plus-parameter - name that is used in the proj-string to - specify the grids to load, e.g. "+grids". - The + should be left out here. - - Returns the number of loaded grids. - -***********************************************/ - - /* prepend "s" to the "grids" string to allow usage with pj_param */ - char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); - sprintf(sgrids, "%s%s", "s", grids); - - if (P->vgridlist_geoid == NULL) { - P->vgridlist_geoid = pj_gridlist_from_nadgrids( - P->ctx, - pj_param(P->ctx, P->params, sgrids).s, - &(P->vgridlist_geoid_count) - ); - - if( P->vgridlist_geoid == NULL || P->vgridlist_geoid_count == 0 ) { - pj_dealloc(sgrids); - return 0; - } - } - - if (P->vgridlist_geoid_count == 0) { - proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); - } - - pj_dealloc(sgrids); - return P->vgridlist_geoid_count; -} - -/***********************************************/ -double proj_vgrid_value(PJ *P, LP lp){ -/*********************************************** - - Read grid value at position lp in grids loaded - with proj_grid_init. - - Returns the grid value of the given coordinate. - -************************************************/ - - struct CTABLE used_grid; - double value; - memset(&used_grid, 0, sizeof(struct CTABLE)); - - value = read_vgrid_value(P, lp, &(P->vgridlist_geoid_count), P->vgridlist_geoid, &used_grid); - proj_log_trace(P, "proj_vgrid_value: (%f, %f) = %f", lp.lam*RAD_TO_DEG, lp.phi*RAD_TO_DEG, value); - - return value; -} diff --git a/src/pj_apply_vgridshift.cpp b/src/pj_apply_vgridshift.cpp new file mode 100644 index 00000000..c1344951 --- /dev/null +++ b/src/pj_apply_vgridshift.cpp @@ -0,0 +1,331 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Apply vertical datum shifts based on grid shift files, normally + * geoid grids mapping WGS84 to NAVD88 or something similar. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "proj_math.h" +#include "proj_internal.h" +#include "projects.h" + +static int is_nodata(float value) +{ + /* nodata? */ + /* GTX official nodata value if -88.88880f, but some grids also */ + /* use other big values for nodata (e.g naptrans2008.gtx has */ + /* nodata values like -2147479936), so test them too */ + return value > 1000 || value < -1000 || value == -88.88880f; +} + +static double read_vgrid_value( PJ *defn, LP input, int *gridlist_count_p, PJ_GRIDINFO **tables, struct CTABLE *ct) { + int itable = 0; + double value = HUGE_VAL; + double grid_x, grid_y; + long grid_ix, grid_iy; + long grid_ix2, grid_iy2; + float *cvs; + /* do not deal with NaN coordinates */ + /* cppcheck-suppress duplicateExpression */ + if( isnan(input.phi) || isnan(input.lam) ) + itable = *gridlist_count_p; + + /* keep trying till we find a table that works */ + for ( ; itable < *gridlist_count_p; itable++ ) + { + PJ_GRIDINFO *gi = tables[itable]; + + ct = gi->ct; + + /* skip tables that don't match our point at all. */ + if( ct->ll.phi > input.phi || ct->ll.lam > input.lam + || ct->ll.phi + (ct->lim.phi-1) * ct->del.phi < input.phi + || ct->ll.lam + (ct->lim.lam-1) * ct->del.lam < input.lam ) + continue; + + /* If we have child nodes, check to see if any of them apply. */ + while( gi->child != NULL ) + { + PJ_GRIDINFO *child; + + for( child = gi->child; child != NULL; child = child->next ) + { + struct CTABLE *ct1 = child->ct; + + if( ct1->ll.phi > input.phi || ct1->ll.lam > input.lam + || ct1->ll.phi+(ct1->lim.phi-1)*ct1->del.phi < input.phi + || ct1->ll.lam+(ct1->lim.lam-1)*ct1->del.lam < input.lam) + continue; + + break; + } + + /* we didn't find a more refined child node to use, so go with current grid */ + if( child == NULL ) + { + break; + } + + /* Otherwise let's try for childrens children .. */ + gi = child; + ct = child->ct; + } + + /* load the grid shift info if we don't have it. */ + if( ct->cvs == NULL && !pj_gridinfo_load( pj_get_ctx(defn), gi ) ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + + + /* Interpolation a location within the grid */ + grid_x = (input.lam - ct->ll.lam) / ct->del.lam; + grid_y = (input.phi - ct->ll.phi) / ct->del.phi; + grid_ix = lround(floor(grid_x)); + grid_iy = lround(floor(grid_y)); + grid_x -= grid_ix; + grid_y -= grid_iy; + + grid_ix2 = grid_ix + 1; + if( grid_ix2 >= ct->lim.lam ) + grid_ix2 = ct->lim.lam - 1; + grid_iy2 = grid_iy + 1; + if( grid_iy2 >= ct->lim.phi ) + grid_iy2 = ct->lim.phi - 1; + + cvs = (float *) ct->cvs; + { + float value_a = cvs[grid_ix + grid_iy * ct->lim.lam]; + float value_b = cvs[grid_ix2 + grid_iy * ct->lim.lam]; + float value_c = cvs[grid_ix + grid_iy2 * ct->lim.lam]; + float value_d = cvs[grid_ix2 + grid_iy2 * ct->lim.lam]; + double total_weight = 0.0; + int n_weights = 0; + value = 0.0f; + if( !is_nodata(value_a) ) + { + double weight = (1.0-grid_x) * (1.0-grid_y); + value += value_a * weight; + total_weight += weight; + n_weights ++; + } + if( !is_nodata(value_b) ) + { + double weight = (grid_x) * (1.0-grid_y); + value += value_b * weight; + total_weight += weight; + n_weights ++; + } + if( !is_nodata(value_c) ) + { + double weight = (1.0-grid_x) * (grid_y); + value += value_c * weight; + total_weight += weight; + n_weights ++; + } + if( !is_nodata(value_d) ) + { + double weight = (grid_x) * (grid_y); + value += value_d * weight; + total_weight += weight; + n_weights ++; + } + if( n_weights == 0 ) + value = HUGE_VAL; + else if( n_weights != 4 ) + value /= total_weight; + } + + } + + return value; +} + +/************************************************************************/ +/* pj_apply_vgridshift() */ +/* */ +/* This implementation takes uses the gridlist from a coordinate */ +/* system definition. If the gridlist has not yet been */ +/* populated in the coordinate system definition we set it up */ +/* now. */ +/************************************************************************/ +int pj_apply_vgridshift( PJ *defn, const char *listname, + PJ_GRIDINFO ***gridlist_p, + int *gridlist_count_p, + int inverse, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + static int debug_count = 0; + PJ_GRIDINFO **tables; + struct CTABLE ct; + + if( *gridlist_p == NULL ) + { + *gridlist_p = + pj_gridlist_from_nadgrids( pj_get_ctx(defn), + pj_param(defn->ctx,defn->params,listname).s, + gridlist_count_p ); + + if( *gridlist_p == NULL || *gridlist_count_p == 0 ) + return defn->ctx->last_errno; + } + + if( *gridlist_count_p == 0 ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + + tables = *gridlist_p; + defn->ctx->last_errno = 0; + + for( i = 0; i < point_count; i++ ) + { + double value; + long io = i * point_offset; + LP input; + + input.phi = y[io]; + input.lam = x[io]; + + value = read_vgrid_value(defn, input, gridlist_count_p, tables, &ct); + + if( inverse ) + z[io] -= value; + else + z[io] += value; + if( value != HUGE_VAL ) + { + if( debug_count++ < 20 ) { + proj_log_trace(defn, "pj_apply_gridshift(): used %s", ct.id); + break; + } + } + + if( value == HUGE_VAL ) + { + int itable; + char gridlist[3000]; + + proj_log_debug(defn, + "pj_apply_vgridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + + gridlist[0] = '\0'; + for( itable = 0; itable < *gridlist_count_p; itable++ ) + { + PJ_GRIDINFO *gi = tables[itable]; + if( strlen(gridlist) + strlen(gi->gridname) > sizeof(gridlist)-100 ) + { + strcat( gridlist, "..." ); + break; + } + + if( itable == 0 ) + sprintf( gridlist, " tried: %s", gi->gridname ); + else + sprintf( gridlist+strlen(gridlist), ",%s", gi->gridname ); + } + + proj_log_debug(defn, "%s", gridlist); + pj_ctx_set_errno( defn->ctx, PJD_ERR_GRID_AREA ); + + return PJD_ERR_GRID_AREA; + } + } + + return 0; +} + +/**********************************************/ +int proj_vgrid_init(PJ* P, const char *grids) { +/********************************************** + + Initizalize and populate gridlist. + + Takes a PJ-object and the plus-parameter + name that is used in the proj-string to + specify the grids to load, e.g. "+grids". + The + should be left out here. + + Returns the number of loaded grids. + +***********************************************/ + + /* prepend "s" to the "grids" string to allow usage with pj_param */ + char *sgrids = (char *) pj_malloc( (strlen(grids)+1+1) *sizeof(char) ); + sprintf(sgrids, "%s%s", "s", grids); + + if (P->vgridlist_geoid == NULL) { + P->vgridlist_geoid = pj_gridlist_from_nadgrids( + P->ctx, + pj_param(P->ctx, P->params, sgrids).s, + &(P->vgridlist_geoid_count) + ); + + if( P->vgridlist_geoid == NULL || P->vgridlist_geoid_count == 0 ) { + pj_dealloc(sgrids); + return 0; + } + } + + if (P->vgridlist_geoid_count == 0) { + proj_errno_set(P, PJD_ERR_FAILED_TO_LOAD_GRID); + } + + pj_dealloc(sgrids); + return P->vgridlist_geoid_count; +} + +/***********************************************/ +double proj_vgrid_value(PJ *P, LP lp){ +/*********************************************** + + Read grid value at position lp in grids loaded + with proj_grid_init. + + Returns the grid value of the given coordinate. + +************************************************/ + + struct CTABLE used_grid; + double value; + memset(&used_grid, 0, sizeof(struct CTABLE)); + + value = read_vgrid_value(P, lp, &(P->vgridlist_geoid_count), P->vgridlist_geoid, &used_grid); + proj_log_trace(P, "proj_vgrid_value: (%f, %f) = %f", lp.lam*RAD_TO_DEG, lp.phi*RAD_TO_DEG, value); + + return value; +} diff --git a/src/pj_auth.c b/src/pj_auth.c deleted file mode 100644 index d6024671..00000000 --- a/src/pj_auth.c +++ /dev/null @@ -1,36 +0,0 @@ -/* determine latitude from authalic latitude */ - -#include -#include - -#include "projects.h" - -# define P00 .33333333333333333333 /* 1 / 3 */ -# define P01 .17222222222222222222 /* 31 / 180 */ -# define P02 .10257936507936507937 /* 517 / 5040 */ -# define P10 .06388888888888888888 /* 23 / 360 */ -# define P11 .06640211640211640212 /* 251 / 3780 */ -# define P20 .01677689594356261023 /* 761 / 45360 */ -#define APA_SIZE 3 - - double * -pj_authset(double es) { - double t, *APA; - - if ((APA = (double *)pj_malloc(APA_SIZE * sizeof(double))) != NULL) { - APA[0] = es * P00; - t = es * es; - APA[0] += t * P01; - APA[1] = t * P10; - t *= es; - APA[0] += t * P02; - APA[1] += t * P11; - APA[2] = t * P20; - } - return APA; -} - double -pj_authlat(double beta, double *APA) { - double t = beta+beta; - return(beta + APA[0] * sin(t) + APA[1] * sin(t+t) + APA[2] * sin(t+t+t)); -} diff --git a/src/pj_auth.cpp b/src/pj_auth.cpp new file mode 100644 index 00000000..d6024671 --- /dev/null +++ b/src/pj_auth.cpp @@ -0,0 +1,36 @@ +/* determine latitude from authalic latitude */ + +#include +#include + +#include "projects.h" + +# define P00 .33333333333333333333 /* 1 / 3 */ +# define P01 .17222222222222222222 /* 31 / 180 */ +# define P02 .10257936507936507937 /* 517 / 5040 */ +# define P10 .06388888888888888888 /* 23 / 360 */ +# define P11 .06640211640211640212 /* 251 / 3780 */ +# define P20 .01677689594356261023 /* 761 / 45360 */ +#define APA_SIZE 3 + + double * +pj_authset(double es) { + double t, *APA; + + if ((APA = (double *)pj_malloc(APA_SIZE * sizeof(double))) != NULL) { + APA[0] = es * P00; + t = es * es; + APA[0] += t * P01; + APA[1] = t * P10; + t *= es; + APA[0] += t * P02; + APA[1] += t * P11; + APA[2] = t * P20; + } + return APA; +} + double +pj_authlat(double beta, double *APA) { + double t = beta+beta; + return(beta + APA[0] * sin(t) + APA[1] * sin(t+t) + APA[2] * sin(t+t+t)); +} diff --git a/src/pj_ctx.c b/src/pj_ctx.c deleted file mode 100644 index 1c99e921..00000000 --- a/src/pj_ctx.c +++ /dev/null @@ -1,233 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the projCtx thread context object. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2010, Frank Warmerdam - * - * 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 -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -static projCtx_t default_context; -static volatile int default_context_initialized = 0; - -/************************************************************************/ -/* pj_get_ctx() */ -/************************************************************************/ - -projCtx pj_get_ctx( projPJ pj ) - -{ - if (0==pj) - return pj_get_default_ctx (); - if (0==pj->ctx) - return pj_get_default_ctx (); - return pj->ctx; -} - -/************************************************************************/ -/* pj_set_ctx() */ -/* */ -/* Note we do not deallocate the old context! */ -/************************************************************************/ - -void pj_set_ctx( projPJ pj, projCtx ctx ) - -{ - if (pj==0) - return; - pj->ctx = ctx; -} - -/************************************************************************/ -/* pj_get_default_ctx() */ -/************************************************************************/ - -projCtx pj_get_default_ctx() - -{ - /* If already initialized, don't bother locking */ - if( default_context_initialized ) - return &default_context; - - pj_acquire_lock(); - - /* Ask again, since it may have been initialized in another thread */ - if( !default_context_initialized ) - { - default_context.last_errno = 0; - default_context.debug_level = PJ_LOG_NONE; - default_context.logger = pj_stderr_logger; - default_context.app_data = NULL; - default_context.fileapi = pj_get_default_fileapi(); - default_context.cpp_context = NULL; - default_context.use_proj4_init_rules = -1; - default_context.epsg_file_exists = -1; - - if( getenv("PROJ_DEBUG") != NULL ) - { - if( atoi(getenv("PROJ_DEBUG")) >= -PJ_LOG_DEBUG_MINOR ) - default_context.debug_level = atoi(getenv("PROJ_DEBUG")); - else - default_context.debug_level = PJ_LOG_DEBUG_MINOR; - } - default_context_initialized = 1; - } - - pj_release_lock(); - - return &default_context; -} - -/************************************************************************/ -/* pj_ctx_alloc() */ -/************************************************************************/ - -projCtx pj_ctx_alloc() - -{ - projCtx ctx = (projCtx_t *) malloc(sizeof(projCtx_t)); - if (0==ctx) - return 0; - memcpy( ctx, pj_get_default_ctx(), sizeof(projCtx_t) ); - ctx->last_errno = 0; - ctx->cpp_context = NULL; - ctx->use_proj4_init_rules = -1; - - return ctx; -} - -/************************************************************************/ -/* pj_ctx_free() */ -/************************************************************************/ - -void pj_ctx_free( projCtx ctx ) - -{ - proj_context_delete_cpp_context( ctx->cpp_context ); - pj_dealloc( ctx ); -} - -/************************************************************************/ -/* pj_ctx_get_errno() */ -/************************************************************************/ - -int pj_ctx_get_errno( projCtx ctx ) - -{ - if (0==ctx) - return pj_get_default_ctx ()->last_errno; - return ctx->last_errno; -} - -/************************************************************************/ -/* pj_ctx_set_errno() */ -/* */ -/* Also sets the global errno */ -/************************************************************************/ - -void pj_ctx_set_errno( projCtx ctx, int new_errno ) - -{ - ctx->last_errno = new_errno; - if( new_errno == 0 ) - return; - errno = new_errno; - pj_errno = new_errno; -} - -/************************************************************************/ -/* pj_ctx_set_debug() */ -/************************************************************************/ - -void pj_ctx_set_debug( projCtx ctx, int new_debug ) - -{ - if (0==ctx) - return; - ctx->debug_level = new_debug; -} - -/************************************************************************/ -/* pj_ctx_set_logger() */ -/************************************************************************/ - -void pj_ctx_set_logger( projCtx ctx, void (*new_logger)(void*,int,const char*) ) - -{ - if (0==ctx) - return; - ctx->logger = new_logger; -} - -/************************************************************************/ -/* pj_ctx_set_app_data() */ -/************************************************************************/ - -void pj_ctx_set_app_data( projCtx ctx, void *new_app_data ) - -{ - if (0==ctx) - return; - ctx->app_data = new_app_data; -} - -/************************************************************************/ -/* pj_ctx_get_app_data() */ -/************************************************************************/ - -void *pj_ctx_get_app_data( projCtx ctx ) - -{ - if (0==ctx) - return 0; - return ctx->app_data; -} - -/************************************************************************/ -/* pj_ctx_set_fileapi() */ -/************************************************************************/ - -void pj_ctx_set_fileapi( projCtx ctx, projFileAPI *fileapi ) - -{ - if (0==ctx) - return; - ctx->fileapi = fileapi; -} - -/************************************************************************/ -/* pj_ctx_get_fileapi() */ -/************************************************************************/ - -projFileAPI *pj_ctx_get_fileapi( projCtx ctx ) - -{ - if (0==ctx) - return 0; - return ctx->fileapi; -} diff --git a/src/pj_ctx.cpp b/src/pj_ctx.cpp new file mode 100644 index 00000000..1c99e921 --- /dev/null +++ b/src/pj_ctx.cpp @@ -0,0 +1,233 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the projCtx thread context object. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * 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 +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +static projCtx_t default_context; +static volatile int default_context_initialized = 0; + +/************************************************************************/ +/* pj_get_ctx() */ +/************************************************************************/ + +projCtx pj_get_ctx( projPJ pj ) + +{ + if (0==pj) + return pj_get_default_ctx (); + if (0==pj->ctx) + return pj_get_default_ctx (); + return pj->ctx; +} + +/************************************************************************/ +/* pj_set_ctx() */ +/* */ +/* Note we do not deallocate the old context! */ +/************************************************************************/ + +void pj_set_ctx( projPJ pj, projCtx ctx ) + +{ + if (pj==0) + return; + pj->ctx = ctx; +} + +/************************************************************************/ +/* pj_get_default_ctx() */ +/************************************************************************/ + +projCtx pj_get_default_ctx() + +{ + /* If already initialized, don't bother locking */ + if( default_context_initialized ) + return &default_context; + + pj_acquire_lock(); + + /* Ask again, since it may have been initialized in another thread */ + if( !default_context_initialized ) + { + default_context.last_errno = 0; + default_context.debug_level = PJ_LOG_NONE; + default_context.logger = pj_stderr_logger; + default_context.app_data = NULL; + default_context.fileapi = pj_get_default_fileapi(); + default_context.cpp_context = NULL; + default_context.use_proj4_init_rules = -1; + default_context.epsg_file_exists = -1; + + if( getenv("PROJ_DEBUG") != NULL ) + { + if( atoi(getenv("PROJ_DEBUG")) >= -PJ_LOG_DEBUG_MINOR ) + default_context.debug_level = atoi(getenv("PROJ_DEBUG")); + else + default_context.debug_level = PJ_LOG_DEBUG_MINOR; + } + default_context_initialized = 1; + } + + pj_release_lock(); + + return &default_context; +} + +/************************************************************************/ +/* pj_ctx_alloc() */ +/************************************************************************/ + +projCtx pj_ctx_alloc() + +{ + projCtx ctx = (projCtx_t *) malloc(sizeof(projCtx_t)); + if (0==ctx) + return 0; + memcpy( ctx, pj_get_default_ctx(), sizeof(projCtx_t) ); + ctx->last_errno = 0; + ctx->cpp_context = NULL; + ctx->use_proj4_init_rules = -1; + + return ctx; +} + +/************************************************************************/ +/* pj_ctx_free() */ +/************************************************************************/ + +void pj_ctx_free( projCtx ctx ) + +{ + proj_context_delete_cpp_context( ctx->cpp_context ); + pj_dealloc( ctx ); +} + +/************************************************************************/ +/* pj_ctx_get_errno() */ +/************************************************************************/ + +int pj_ctx_get_errno( projCtx ctx ) + +{ + if (0==ctx) + return pj_get_default_ctx ()->last_errno; + return ctx->last_errno; +} + +/************************************************************************/ +/* pj_ctx_set_errno() */ +/* */ +/* Also sets the global errno */ +/************************************************************************/ + +void pj_ctx_set_errno( projCtx ctx, int new_errno ) + +{ + ctx->last_errno = new_errno; + if( new_errno == 0 ) + return; + errno = new_errno; + pj_errno = new_errno; +} + +/************************************************************************/ +/* pj_ctx_set_debug() */ +/************************************************************************/ + +void pj_ctx_set_debug( projCtx ctx, int new_debug ) + +{ + if (0==ctx) + return; + ctx->debug_level = new_debug; +} + +/************************************************************************/ +/* pj_ctx_set_logger() */ +/************************************************************************/ + +void pj_ctx_set_logger( projCtx ctx, void (*new_logger)(void*,int,const char*) ) + +{ + if (0==ctx) + return; + ctx->logger = new_logger; +} + +/************************************************************************/ +/* pj_ctx_set_app_data() */ +/************************************************************************/ + +void pj_ctx_set_app_data( projCtx ctx, void *new_app_data ) + +{ + if (0==ctx) + return; + ctx->app_data = new_app_data; +} + +/************************************************************************/ +/* pj_ctx_get_app_data() */ +/************************************************************************/ + +void *pj_ctx_get_app_data( projCtx ctx ) + +{ + if (0==ctx) + return 0; + return ctx->app_data; +} + +/************************************************************************/ +/* pj_ctx_set_fileapi() */ +/************************************************************************/ + +void pj_ctx_set_fileapi( projCtx ctx, projFileAPI *fileapi ) + +{ + if (0==ctx) + return; + ctx->fileapi = fileapi; +} + +/************************************************************************/ +/* pj_ctx_get_fileapi() */ +/************************************************************************/ + +projFileAPI *pj_ctx_get_fileapi( projCtx ctx ) + +{ + if (0==ctx) + return 0; + return ctx->fileapi; +} diff --git a/src/pj_datum_set.c b/src/pj_datum_set.c deleted file mode 100644 index 466b56c5..00000000 --- a/src/pj_datum_set.c +++ /dev/null @@ -1,169 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Apply datum definition to PJ structure from initialization string. - * Author: Frank Warmerdam, warmerda@home.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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 -#include - -#include "projects.h" - -/* SEC_TO_RAD = Pi/180/3600 */ -#define SEC_TO_RAD 4.84813681109535993589914102357e-6 - -/************************************************************************/ -/* pj_datum_set() */ -/************************************************************************/ - -int pj_datum_set(projCtx ctx, paralist *pl, PJ *projdef) - -{ - const char *name, *towgs84, *nadgrids, *catalog; - - projdef->datum_type = PJD_UNKNOWN; - -/* -------------------------------------------------------------------- */ -/* Is there a datum definition in the parameters list? If so, */ -/* add the defining values to the parameter list. Note that */ -/* this will append the ellipse definition as well as the */ -/* towgs84= and related parameters. It should also be pointed */ -/* out that the addition is permanent rather than temporary */ -/* like most other keyword expansion so that the ellipse */ -/* definition will last into the pj_ell_set() function called */ -/* after this one. */ -/* -------------------------------------------------------------------- */ - if( (name = pj_param(ctx, pl,"sdatum").s) != NULL ) - { - paralist *curr; - const char *s; - int i; - - /* find the end of the list, so we can add to it */ - for (curr = pl; curr && curr->next ; curr = curr->next) {} - - /* cannot happen in practice, but makes static analyzers happy */ - if( !curr ) return -1; - - /* find the datum definition */ - for (i = 0; (s = pj_datums[i].id) && strcmp(name, s) ; ++i) {} - - if (!s) { - pj_ctx_set_errno(ctx, PJD_ERR_UNKNOWN_ELLP_PARAM); - return 1; - } - - if( pj_datums[i].ellipse_id && strlen(pj_datums[i].ellipse_id) > 0 ) - { - char entry[100]; - - strcpy( entry, "ellps=" ); - strncpy( entry + strlen(entry), pj_datums[i].ellipse_id, - sizeof(entry) - 1 - strlen(entry) ); - entry[ sizeof(entry) - 1 ] = '\0'; - - curr = curr->next = pj_mkparam(entry); - } - - if( pj_datums[i].defn && strlen(pj_datums[i].defn) > 0 ) - curr = curr->next = pj_mkparam(pj_datums[i].defn); - - (void)curr; /* make clang static analyzer happy */ - } - -/* -------------------------------------------------------------------- */ -/* Check for nadgrids parameter. */ -/* -------------------------------------------------------------------- */ - nadgrids = pj_param(ctx, pl,"snadgrids").s; - if( nadgrids != NULL ) - { - /* We don't actually save the value separately. It will continue - to exist int he param list for use in pj_apply_gridshift.c */ - - projdef->datum_type = PJD_GRIDSHIFT; - } - -/* -------------------------------------------------------------------- */ -/* Check for grid catalog parameter, and optional date. */ -/* -------------------------------------------------------------------- */ - else if( (catalog = pj_param(ctx, pl,"scatalog").s) != NULL ) - { - const char *date; - - projdef->datum_type = PJD_GRIDSHIFT; - projdef->catalog_name = pj_strdup(catalog); - if (!projdef->catalog_name) { - pj_ctx_set_errno(ctx, ENOMEM); - return 1; - } - - date = pj_param(ctx, pl, "sdate").s; - if( date != NULL) - projdef->datum_date = pj_gc_parsedate( ctx, date); - } - -/* -------------------------------------------------------------------- */ -/* Check for towgs84 parameter. */ -/* -------------------------------------------------------------------- */ - else if( (towgs84 = pj_param(ctx, pl,"stowgs84").s) != NULL ) - { - int parm_count = 0; - const char *s; - - memset( projdef->datum_params, 0, sizeof(double) * 7); - - /* parse out the parameters */ - for( s = towgs84; *s != '\0' && parm_count < 7; ) - { - projdef->datum_params[parm_count++] = pj_atof(s); - while( *s != '\0' && *s != ',' ) - s++; - if( *s == ',' ) - s++; - } - - if( projdef->datum_params[3] != 0.0 - || projdef->datum_params[4] != 0.0 - || projdef->datum_params[5] != 0.0 - || projdef->datum_params[6] != 0.0 ) - { - projdef->datum_type = PJD_7PARAM; - - /* transform from arc seconds to radians */ - projdef->datum_params[3] *= SEC_TO_RAD; - projdef->datum_params[4] *= SEC_TO_RAD; - projdef->datum_params[5] *= SEC_TO_RAD; - /* transform from parts per million to scaling factor */ - projdef->datum_params[6] = - (projdef->datum_params[6]/1000000.0) + 1; - } - else - projdef->datum_type = PJD_3PARAM; - - /* Note that pj_init() will later switch datum_type to - PJD_WGS84 if shifts are all zero, and ellipsoid is WGS84 or GRS80 */ - } - - return 0; -} diff --git a/src/pj_datum_set.cpp b/src/pj_datum_set.cpp new file mode 100644 index 00000000..466b56c5 --- /dev/null +++ b/src/pj_datum_set.cpp @@ -0,0 +1,169 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Apply datum definition to PJ structure from initialization string. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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 +#include + +#include "projects.h" + +/* SEC_TO_RAD = Pi/180/3600 */ +#define SEC_TO_RAD 4.84813681109535993589914102357e-6 + +/************************************************************************/ +/* pj_datum_set() */ +/************************************************************************/ + +int pj_datum_set(projCtx ctx, paralist *pl, PJ *projdef) + +{ + const char *name, *towgs84, *nadgrids, *catalog; + + projdef->datum_type = PJD_UNKNOWN; + +/* -------------------------------------------------------------------- */ +/* Is there a datum definition in the parameters list? If so, */ +/* add the defining values to the parameter list. Note that */ +/* this will append the ellipse definition as well as the */ +/* towgs84= and related parameters. It should also be pointed */ +/* out that the addition is permanent rather than temporary */ +/* like most other keyword expansion so that the ellipse */ +/* definition will last into the pj_ell_set() function called */ +/* after this one. */ +/* -------------------------------------------------------------------- */ + if( (name = pj_param(ctx, pl,"sdatum").s) != NULL ) + { + paralist *curr; + const char *s; + int i; + + /* find the end of the list, so we can add to it */ + for (curr = pl; curr && curr->next ; curr = curr->next) {} + + /* cannot happen in practice, but makes static analyzers happy */ + if( !curr ) return -1; + + /* find the datum definition */ + for (i = 0; (s = pj_datums[i].id) && strcmp(name, s) ; ++i) {} + + if (!s) { + pj_ctx_set_errno(ctx, PJD_ERR_UNKNOWN_ELLP_PARAM); + return 1; + } + + if( pj_datums[i].ellipse_id && strlen(pj_datums[i].ellipse_id) > 0 ) + { + char entry[100]; + + strcpy( entry, "ellps=" ); + strncpy( entry + strlen(entry), pj_datums[i].ellipse_id, + sizeof(entry) - 1 - strlen(entry) ); + entry[ sizeof(entry) - 1 ] = '\0'; + + curr = curr->next = pj_mkparam(entry); + } + + if( pj_datums[i].defn && strlen(pj_datums[i].defn) > 0 ) + curr = curr->next = pj_mkparam(pj_datums[i].defn); + + (void)curr; /* make clang static analyzer happy */ + } + +/* -------------------------------------------------------------------- */ +/* Check for nadgrids parameter. */ +/* -------------------------------------------------------------------- */ + nadgrids = pj_param(ctx, pl,"snadgrids").s; + if( nadgrids != NULL ) + { + /* We don't actually save the value separately. It will continue + to exist int he param list for use in pj_apply_gridshift.c */ + + projdef->datum_type = PJD_GRIDSHIFT; + } + +/* -------------------------------------------------------------------- */ +/* Check for grid catalog parameter, and optional date. */ +/* -------------------------------------------------------------------- */ + else if( (catalog = pj_param(ctx, pl,"scatalog").s) != NULL ) + { + const char *date; + + projdef->datum_type = PJD_GRIDSHIFT; + projdef->catalog_name = pj_strdup(catalog); + if (!projdef->catalog_name) { + pj_ctx_set_errno(ctx, ENOMEM); + return 1; + } + + date = pj_param(ctx, pl, "sdate").s; + if( date != NULL) + projdef->datum_date = pj_gc_parsedate( ctx, date); + } + +/* -------------------------------------------------------------------- */ +/* Check for towgs84 parameter. */ +/* -------------------------------------------------------------------- */ + else if( (towgs84 = pj_param(ctx, pl,"stowgs84").s) != NULL ) + { + int parm_count = 0; + const char *s; + + memset( projdef->datum_params, 0, sizeof(double) * 7); + + /* parse out the parameters */ + for( s = towgs84; *s != '\0' && parm_count < 7; ) + { + projdef->datum_params[parm_count++] = pj_atof(s); + while( *s != '\0' && *s != ',' ) + s++; + if( *s == ',' ) + s++; + } + + if( projdef->datum_params[3] != 0.0 + || projdef->datum_params[4] != 0.0 + || projdef->datum_params[5] != 0.0 + || projdef->datum_params[6] != 0.0 ) + { + projdef->datum_type = PJD_7PARAM; + + /* transform from arc seconds to radians */ + projdef->datum_params[3] *= SEC_TO_RAD; + projdef->datum_params[4] *= SEC_TO_RAD; + projdef->datum_params[5] *= SEC_TO_RAD; + /* transform from parts per million to scaling factor */ + projdef->datum_params[6] = + (projdef->datum_params[6]/1000000.0) + 1; + } + else + projdef->datum_type = PJD_3PARAM; + + /* Note that pj_init() will later switch datum_type to + PJD_WGS84 if shifts are all zero, and ellipsoid is WGS84 or GRS80 */ + } + + return 0; +} diff --git a/src/pj_datums.c b/src/pj_datums.c deleted file mode 100644 index 2951b7bd..00000000 --- a/src/pj_datums.c +++ /dev/null @@ -1,99 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Built in datum list. - * Author: Frank Warmerdam, warmerda@home.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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 - -#include "proj.h" - -#define PJ_DATUMS__ -#include "projects.h" - -/* - * The ellipse code must match one from pj_ellps.c. The datum id should - * be kept to 12 characters or less if possible. Use the official OGC - * datum name for the comments if available. - */ - -C_NAMESPACE_VAR const struct PJ_DATUMS pj_datums[] = { -/* id definition ellipse comments */ -/* -- ---------- ------- -------- */ -{"WGS84", "towgs84=0,0,0", "WGS84", ""}, -{"GGRS87", "towgs84=-199.87,74.79,246.62", "GRS80", - "Greek_Geodetic_Reference_System_1987"}, -{"NAD83", "towgs84=0,0,0", "GRS80", - "North_American_Datum_1983"}, -{"NAD27", "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", - "clrk66", - "North_American_Datum_1927"}, -{"potsdam", /*"towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7",*/ - "nadgrids=@BETA2007.gsb", - "bessel", - "Potsdam Rauenberg 1950 DHDN"}, -{"carthage","towgs84=-263.0,6.0,431.0", "clrk80ign", - "Carthage 1934 Tunisia"}, -{"hermannskogel", "towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232", - "bessel", - "Hermannskogel"}, -{"ire65", "towgs84=482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", - "mod_airy", - "Ireland 1965"}, -{"nzgd49", "towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", - "intl", "New Zealand Geodetic Datum 1949"}, -{"OSGB36", "towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", - "airy", "Airy 1830"}, -{NULL, NULL, NULL, NULL} -}; - -struct PJ_DATUMS *pj_get_datums_ref() -{ - return (struct PJ_DATUMS *)pj_datums; -} - -static const struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { - /* id definition */ - /* -- ---------- */ - {"greenwich", "0dE"}, - {"lisbon", "9d07'54.862\"W"}, - {"paris", "2d20'14.025\"E"}, - {"bogota", "74d04'51.3\"W"}, - {"madrid", "3d41'16.58\"W"}, - {"rome", "12d27'8.4\"E"}, - {"bern", "7d26'22.5\"E"}, - {"jakarta", "106d48'27.79\"E"}, - {"ferro", "17d40'W"}, - {"brussels", "4d22'4.71\"E"}, - {"stockholm", "18d3'29.8\"E"}, - {"athens", "23d42'58.815\"E"}, - {"oslo", "10d43'22.5\"E"}, - {"copenhagen","12d34'40.35\"E"}, - {NULL, NULL} -}; - -const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) -{ - return pj_prime_meridians; -} diff --git a/src/pj_datums.cpp b/src/pj_datums.cpp new file mode 100644 index 00000000..2951b7bd --- /dev/null +++ b/src/pj_datums.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Built in datum list. + * Author: Frank Warmerdam, warmerda@home.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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 + +#include "proj.h" + +#define PJ_DATUMS__ +#include "projects.h" + +/* + * The ellipse code must match one from pj_ellps.c. The datum id should + * be kept to 12 characters or less if possible. Use the official OGC + * datum name for the comments if available. + */ + +C_NAMESPACE_VAR const struct PJ_DATUMS pj_datums[] = { +/* id definition ellipse comments */ +/* -- ---------- ------- -------- */ +{"WGS84", "towgs84=0,0,0", "WGS84", ""}, +{"GGRS87", "towgs84=-199.87,74.79,246.62", "GRS80", + "Greek_Geodetic_Reference_System_1987"}, +{"NAD83", "towgs84=0,0,0", "GRS80", + "North_American_Datum_1983"}, +{"NAD27", "nadgrids=@conus,@alaska,@ntv2_0.gsb,@ntv1_can.dat", + "clrk66", + "North_American_Datum_1927"}, +{"potsdam", /*"towgs84=598.1,73.7,418.2,0.202,0.045,-2.455,6.7",*/ + "nadgrids=@BETA2007.gsb", + "bessel", + "Potsdam Rauenberg 1950 DHDN"}, +{"carthage","towgs84=-263.0,6.0,431.0", "clrk80ign", + "Carthage 1934 Tunisia"}, +{"hermannskogel", "towgs84=577.326,90.129,463.919,5.137,1.474,5.297,2.4232", + "bessel", + "Hermannskogel"}, +{"ire65", "towgs84=482.530,-130.596,564.557,-1.042,-0.214,-0.631,8.15", + "mod_airy", + "Ireland 1965"}, +{"nzgd49", "towgs84=59.47,-5.04,187.44,0.47,-0.1,1.024,-4.5993", + "intl", "New Zealand Geodetic Datum 1949"}, +{"OSGB36", "towgs84=446.448,-125.157,542.060,0.1502,0.2470,0.8421,-20.4894", + "airy", "Airy 1830"}, +{NULL, NULL, NULL, NULL} +}; + +struct PJ_DATUMS *pj_get_datums_ref() +{ + return (struct PJ_DATUMS *)pj_datums; +} + +static const struct PJ_PRIME_MERIDIANS pj_prime_meridians[] = { + /* id definition */ + /* -- ---------- */ + {"greenwich", "0dE"}, + {"lisbon", "9d07'54.862\"W"}, + {"paris", "2d20'14.025\"E"}, + {"bogota", "74d04'51.3\"W"}, + {"madrid", "3d41'16.58\"W"}, + {"rome", "12d27'8.4\"E"}, + {"bern", "7d26'22.5\"E"}, + {"jakarta", "106d48'27.79\"E"}, + {"ferro", "17d40'W"}, + {"brussels", "4d22'4.71\"E"}, + {"stockholm", "18d3'29.8\"E"}, + {"athens", "23d42'58.815\"E"}, + {"oslo", "10d43'22.5\"E"}, + {"copenhagen","12d34'40.35\"E"}, + {NULL, NULL} +}; + +const PJ_PRIME_MERIDIANS *proj_list_prime_meridians(void) +{ + return pj_prime_meridians; +} diff --git a/src/pj_deriv.c b/src/pj_deriv.c deleted file mode 100644 index 2c91e0cc..00000000 --- a/src/pj_deriv.c +++ /dev/null @@ -1,70 +0,0 @@ -/* dervative of (*P->fwd) projection */ -#define PJ_LIB__ - -#include - -#include "projects.h" - -int pj_deriv(LP lp, double h, const PJ *P, struct DERIVS *der) { - XY t; - /* get rid of constness until we can do it for real */ - PJ *Q = (PJ *) P; - if (0==Q->fwd) - return 1; - - lp.lam += h; - lp.phi += h; - if (fabs(lp.phi) > M_HALFPI) - return 1; - - h += h; - t = (*Q->fwd)(lp, Q); - if (t.x == HUGE_VAL) - return 1; - - der->x_l = t.x; - der->y_p = t.y; - der->x_p = t.x; - der->y_l = t.y; - - lp.phi -= h; - if (fabs(lp.phi) > M_HALFPI) - return 1; - - t = (*Q->fwd)(lp, Q); - if (t.x == HUGE_VAL) - return 1; - - der->x_l += t.x; - der->y_p -= t.y; - der->x_p -= t.x; - der->y_l += t.y; - - lp.lam -= h; - t = (*Q->fwd)(lp, Q); - if (t.x == HUGE_VAL) - return 1; - - der->x_l -= t.x; - der->y_p -= t.y; - der->x_p -= t.x; - der->y_l -= t.y; - - lp.phi += h; - t = (*Q->fwd)(lp, Q); - if (t.x == HUGE_VAL) - return 1; - - der->x_l -= t.x; - der->y_p += t.y; - der->x_p += t.x; - der->y_l -= t.y; - - h += h; - der->x_l /= h; - der->y_p /= h; - der->x_p /= h; - der->y_l /= h; - - return 0; -} diff --git a/src/pj_deriv.cpp b/src/pj_deriv.cpp new file mode 100644 index 00000000..2c91e0cc --- /dev/null +++ b/src/pj_deriv.cpp @@ -0,0 +1,70 @@ +/* dervative of (*P->fwd) projection */ +#define PJ_LIB__ + +#include + +#include "projects.h" + +int pj_deriv(LP lp, double h, const PJ *P, struct DERIVS *der) { + XY t; + /* get rid of constness until we can do it for real */ + PJ *Q = (PJ *) P; + if (0==Q->fwd) + return 1; + + lp.lam += h; + lp.phi += h; + if (fabs(lp.phi) > M_HALFPI) + return 1; + + h += h; + t = (*Q->fwd)(lp, Q); + if (t.x == HUGE_VAL) + return 1; + + der->x_l = t.x; + der->y_p = t.y; + der->x_p = t.x; + der->y_l = t.y; + + lp.phi -= h; + if (fabs(lp.phi) > M_HALFPI) + return 1; + + t = (*Q->fwd)(lp, Q); + if (t.x == HUGE_VAL) + return 1; + + der->x_l += t.x; + der->y_p -= t.y; + der->x_p -= t.x; + der->y_l += t.y; + + lp.lam -= h; + t = (*Q->fwd)(lp, Q); + if (t.x == HUGE_VAL) + return 1; + + der->x_l -= t.x; + der->y_p -= t.y; + der->x_p -= t.x; + der->y_l -= t.y; + + lp.phi += h; + t = (*Q->fwd)(lp, Q); + if (t.x == HUGE_VAL) + return 1; + + der->x_l -= t.x; + der->y_p += t.y; + der->x_p += t.x; + der->y_l -= t.y; + + h += h; + der->x_l /= h; + der->y_p /= h; + der->x_p /= h; + der->y_l /= h; + + return 0; +} diff --git a/src/pj_ell_set.c b/src/pj_ell_set.c deleted file mode 100644 index e2d2750c..00000000 --- a/src/pj_ell_set.c +++ /dev/null @@ -1,727 +0,0 @@ -/* set ellipsoid parameters a and es */ - -#include -#include -#include - -#include "proj.h" -#include "proj_internal.h" -#include "projects.h" - - -/* Prototypes of the pj_ellipsoid helper functions */ -static int ellps_ellps (PJ *P); -static int ellps_size (PJ *P); -static int ellps_shape (PJ *P); -static int ellps_spherification (PJ *P); - -static paralist *pj_get_param (paralist *list, char *key); -static char *pj_param_value (paralist *list); -static const PJ_ELLPS *pj_find_ellps (char *name); - - -/***************************************************************************************/ -int pj_ellipsoid (PJ *P) { -/**************************************************************************************** - This is a replacement for the classic PROJ pj_ell_set function. The main difference - is that pj_ellipsoid augments the PJ object with a copy of the exact tags used to - define its related ellipsoid. - - This makes it possible to let a new PJ object inherit the geometrical properties - of an existing one. - - A complete ellipsoid definition comprises a size (primary) and a shape (secondary) - parameter. - - Size parameters supported are: - R, defining the radius of a spherical planet - a, defining the semimajor axis of an ellipsoidal planet - - Shape parameters supported are: - rf, the reverse flattening of the ellipsoid - f, the flattening of the ellipsoid - es, the eccentricity squared - e, the eccentricity - b, the semiminor axis - - The ellps=xxx parameter provides both size and shape for a number of built in - ellipsoid definitions. - - The ellipsoid definition may be augmented with a spherification flag, turning - the ellipsoid into a sphere with features defined by the ellipsoid. - - Spherification parameters supported are: - R_A, which gives a sphere with the same surface area as the ellipsoid - R_A, which gives a sphere with the same volume as the ellipsoid - - R_a, which gives a sphere with R = (a + b)/2 (arithmetic mean) - R_g, which gives a sphere with R = sqrt(a*b) (geometric mean) - R_h, which gives a sphere with R = 2*a*b/(a+b) (harmonic mean) - - R_lat_a=phi, which gives a sphere with R being the arithmetic mean of - of the corresponding ellipsoid at latitude phi. - R_lat_g=phi, which gives a sphere with R being the geometric mean of - of the corresponding ellipsoid at latitude phi. - - If R is given as size parameter, any shape and spherification parameters - given are ignored. - - If size and shape are given as ellps=xxx, later shape and size parameters - are are taken into account as modifiers for the built in ellipsoid definition. - - While this may seem strange, it is in accordance with historical PROJ - behaviour. It can e.g. be used to define coordinates on the ellipsoid - scaled to unit semimajor axis by specifying "+ellps=xxx +a=1" - -****************************************************************************************/ - int err = proj_errno_reset (P); - char *empty = {""}; - - P->def_size = P->def_shape = P->def_spherification = P->def_ellps = 0; - - /* Specifying R overrules everything */ - if (pj_get_param (P->params, "R")) { - ellps_size (P); - pj_calc_ellipsoid_params (P, P->a, 0); - if (proj_errno (P)) - return 1; - return proj_errno_restore (P, err); - } - - - /* If an ellps argument is specified, start by using that */ - if (0 != ellps_ellps (P)) - return 1; - - /* We may overwrite the size */ - if (0 != ellps_size (P)) - return 2; - - /* We may also overwrite the shape */ - if (0 != ellps_shape (P)) - return 3; - - /* When we're done with it, we compute all related ellipsoid parameters */ - pj_calc_ellipsoid_params (P, P->a, P->es); - - /* And finally, we may turn it into a sphere */ - if (0 != ellps_spherification (P)) - return 4; - - proj_log_debug (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d", - P->a, P->f!=0? 1/P->f: 0, proj_errno (P)); - proj_log_debug (P, "pj_ellipsoid - final: %s %s %s %s", - P->def_size? P->def_size: empty, - P->def_shape? P->def_shape: empty, - P->def_spherification? P->def_spherification: empty, - P->def_ellps? P->def_ellps: empty ); - - if (proj_errno (P)) - return 5; - - /* success */ - return proj_errno_restore (P, err); -} - - -/***************************************************************************************/ -static int ellps_ellps (PJ *P) { -/***************************************************************************************/ - PJ B; - const PJ_ELLPS *ellps; - paralist *par = 0; - char *name; - int err; - - /* Sail home if ellps=xxx is not specified */ - par = pj_get_param (P->params, "ellps"); - if (0==par) - return 0; - - /* Otherwise produce a fake PJ to make ellps_size/ellps_shape do the hard work for us */ - - /* First move B into P's context to get error messages onto the right channel */ - B.ctx = P->ctx; - - /* Then look up the right size and shape parameters from the builtin list */ - if (strlen (par->param) < 7) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - name = par->param + 6; - ellps = pj_find_ellps (name); - if (0==ellps) - return proj_errno_set (P, PJD_ERR_UNKNOWN_ELLP_PARAM); - - /* Now, get things ready for ellps_size/ellps_shape, make them do their thing, and clean up */ - err = proj_errno_reset (P); - B = *P; - pj_erase_ellipsoid_def (&B); - B.params = pj_mkparam (ellps->major); - B.params->next = pj_mkparam (ellps->ell); - - ellps_size (&B); - ellps_shape (&B); - - pj_dealloc (B.params->next); - pj_dealloc (B.params); - if (proj_errno (&B)) - return proj_errno (&B); - - /* Finally update P and sail home */ - pj_inherit_ellipsoid_def (&B, P); - P->def_ellps = par->param; - par->used = 1; - - return proj_errno_restore (P, err); -} - - -/***************************************************************************************/ -static int ellps_size (PJ *P) { -/***************************************************************************************/ - paralist *par = 0; - int a_was_set = 0; - - /* A size parameter *must* be given, but may have been given as ellps prior */ - if (P->a != 0) - a_was_set = 1; - - /* Check which size key is specified */ - par = pj_get_param (P->params, "R"); - if (0==par) - par = pj_get_param (P->params, "a"); - if (0==par) - return a_was_set? 0: proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); - - P->def_size = par->param; - par->used = 1; - P->a = pj_atof (pj_param_value (par)); - if (P->a <= 0) - return proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); - if (HUGE_VAL==P->a) - return proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); - - if ('R'==par->param[0]) { - P->es = P->f = P->e = P->rf = 0; - P->b = P->a; - } - return 0; -} - - -/***************************************************************************************/ -static int ellps_shape (PJ *P) { -/***************************************************************************************/ - char *keys[] = {"rf", "f", "es", "e", "b"}; - paralist *par = 0; - char *def = 0; - size_t i, len; - - par = 0; - len = sizeof (keys) / sizeof (char *); - - /* Check which shape key is specified */ - for (i = 0; i < len; i++) { - par = pj_get_param (P->params, keys[i]); - if (par) - break; - } - - /* Not giving a shape parameter means selecting a sphere, unless shape */ - /* has been selected previously via ellps=xxx */ - if (0==par && P->es != 0) - return 0; - if (0==par && P->es==0) { - P->es = P->f = 0; - P->b = P->a; - return 0; - } - - P->def_shape = def = par->param; - par->used = 1; - P->es = P->f = P->b = P->e = P->rf = 0; - - switch (i) { - - /* reverse flattening, rf */ - case 0: - P->rf = pj_atof (pj_param_value (par)); - if (HUGE_VAL==P->rf) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - if (0==P->rf) - return proj_errno_set (P, PJD_ERR_REV_FLATTENING_IS_ZERO); - P->f = 1 / P->rf; - P->es = 2*P->f - P->f*P->f; - break; - - /* flattening, f */ - case 1: - P->f = pj_atof (pj_param_value (par)); - if (HUGE_VAL==P->f) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - - P->rf = P->f != 0.0 ? 1.0/P->f: HUGE_VAL; - P->es = 2*P->f - P->f*P->f; - break; - - /* eccentricity squared, es */ - case 2: - P->es = pj_atof (pj_param_value (par)); - if (HUGE_VAL==P->es) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - if (1==P->es) - return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); - break; - - /* eccentricity, e */ - case 3: - P->e = pj_atof (pj_param_value (par)); - if (HUGE_VAL==P->e) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - if (0==P->e) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - if (1==P->e) - return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); - P->es = P->e * P->e; - break; - - /* semiminor axis, b */ - case 4: - P->b = pj_atof (pj_param_value (par)); - if (HUGE_VAL==P->b) - return proj_errno_set (P, PJD_ERR_INVALID_ARG); - if (0==P->b) - return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); - if (P->b==P->a) - break; - P->f = (P->a - P->b) / P->a; - P->es = 2*P->f - P->f*P->f; - break; - default: - return PJD_ERR_INVALID_ARG; - - } - - if (P->es < 0) - return proj_errno_set (P, PJD_ERR_ES_LESS_THAN_ZERO); - return 0; -} - - -/* series coefficients for calculating ellipsoid-equivalent spheres */ -static const double SIXTH = 1/6.; -static const double RA4 = 17/360.; -static const double RA6 = 67/3024.; -static const double RV4 = 5/72.; -static const double RV6 = 55/1296.; - -/***************************************************************************************/ -static int ellps_spherification (PJ *P) { -/***************************************************************************************/ - char *keys[] = {"R_A", "R_V", "R_a", "R_g", "R_h", "R_lat_a", "R_lat_g"}; - size_t len, i; - paralist *par = 0; - char *def = 0; - - double t; - char *v, *endp; - - len = sizeof (keys) / sizeof (char *); - P->def_spherification = 0; - - /* Check which spherification key is specified */ - for (i = 0; i < len; i++) { - par = pj_get_param (P->params, keys[i]); - if (par) - break; - } - - /* No spherification specified? Then we're done */ - if (i==len) - return 0; - - /* Store definition */ - P->def_spherification = def = par->param; - par->used = 1; - - switch (i) { - - /* R_A - a sphere with same area as ellipsoid */ - case 0: - P->a *= 1. - P->es * (SIXTH + P->es * (RA4 + P->es * RA6)); - break; - - /* R_V - a sphere with same volume as ellipsoid */ - case 1: - P->a *= 1. - P->es * (SIXTH + P->es * (RV4 + P->es * RV6)); - break; - - /* R_a - a sphere with R = the arithmetic mean of the ellipsoid */ - case 2: - P->a = (P->a + P->b) / 2; - break; - - /* R_g - a sphere with R = the geometric mean of the ellipsoid */ - case 3: - P->a = sqrt (P->a * P->b); - break; - - /* R_h - a sphere with R = the harmonic mean of the ellipsoid */ - case 4: - if (P->a + P->b == 0) - return proj_errno_set (P, PJD_ERR_TOLERANCE_CONDITION); - P->a = (2*P->a * P->b) / (P->a + P->b); - break; - - /* R_lat_a - a sphere with R = the arithmetic mean of the ellipsoid at given latitude */ - case 5: - /* R_lat_g - a sphere with R = the geometric mean of the ellipsoid at given latitude */ - case 6: - v = pj_param_value (par); - t = proj_dmstor (v, &endp); - if (fabs (t) > M_HALFPI) - return proj_errno_set (P, PJD_ERR_REF_RAD_LARGER_THAN_90); - t = sin (t); - t = 1 - P->es * t * t; - if (i==5) /* arithmetic */ - P->a *= (1. - P->es + t) / (2 * t * sqrt(t)); - else /* geometric */ - P->a *= sqrt (1 - P->es) / t; - break; - } - - /* Clean up the ellipsoidal parameters to reflect the sphere */ - P->es = P->e = P->f = 0; - P->rf = HUGE_VAL; - P->b = P->a; - pj_calc_ellipsoid_params (P, P->a, 0); - - return 0; -} - - -/* locate parameter in list */ -static paralist *pj_get_param (paralist *list, char *key) { - size_t l = strlen(key); - while (list && !(0==strncmp(list->param, key, l) && (0==list->param[l] || list->param[l] == '=') ) ) - list = list->next; - return list; -} - - -static char *pj_param_value (paralist *list) { - char *key, *value; - if (0==list) - return 0; - - key = list->param; - value = strchr (key, '='); - - /* a flag (i.e. a key without value) has its own name (key) as value */ - return value? value + 1: key; -} - - -static const PJ_ELLPS *pj_find_ellps (char *name) { - int i; - const char *s; - const PJ_ELLPS *ellps; - - if (0==name) - return 0; - - ellps = proj_list_ellps(); - - /* Search through internal ellipsoid list for name */ - for (i = 0; (s = ellps[i].id) && strcmp(name, s) ; ++i); - if (0==s) - return 0; - return ellps + i; -} - - -/**************************************************************************************/ -void pj_erase_ellipsoid_def (PJ *P) { -/*************************************************************************************** - Erase all ellipsoidal parameters in P -***************************************************************************************/ - PJ B; - - /* Make a blank PJ to copy from */ - memset (&B, 0, sizeof (B)); - - /* And use it to overwrite all existing ellipsoid defs */ - pj_inherit_ellipsoid_def (&B, P); -} - - -/**************************************************************************************/ -void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst) { -/*************************************************************************************** - Brute force copy the ellipsoidal parameters from src to dst. This code was - written before the actual ellipsoid setup parameters were kept available in - the PJ->def_xxx elements. -***************************************************************************************/ - - /* The linear parameters */ - dst->a = src->a; - dst->b = src->b; - dst->ra = src->ra; - dst->rb = src->rb; - - /* The eccentricities */ - dst->alpha = src->alpha; - dst->e = src->e; - dst->es = src->es; - dst->e2 = src->e2; - dst->e2s = src->e2s; - dst->e3 = src->e3; - dst->e3s = src->e3s; - dst->one_es = src->one_es; - dst->rone_es = src->rone_es; - - /* The flattenings */ - dst->f = src->f; - dst->f2 = src->f2; - dst->n = src->n; - dst->rf = src->rf; - dst->rf2 = src->rf2; - dst->rn = src->rn; - - /* This one's for GRS80 */ - dst->J = src->J; - - /* es and a before any +proj related adjustment */ - dst->es_orig = src->es_orig; - dst->a_orig = src->a_orig; -} - - -/***************************************************************************************/ -int pj_calc_ellipsoid_params (PJ *P, double a, double es) { -/**************************************************************************************** - Calculate a large number of ancillary ellipsoidal parameters, in addition to - the two traditional PROJ defining parameters: Semimajor axis, a, and the - eccentricity squared, es. - - Most of these parameters are fairly cheap to compute in comparison to the overall - effort involved in initializing a PJ object. They may, however, take a substantial - part of the time taken in computing an individual point transformation. - - So by providing them up front, we can amortize the (already modest) cost over all - transformations carried out over the entire lifetime of a PJ object, rather than - incur that cost for every single transformation. - - Most of the parameter calculations here are based on the "angular eccentricity", - i.e. the angle, measured from the semiminor axis, of a line going from the north - pole to one of the foci of the ellipsoid - or in other words: The arc sine of the - eccentricity. - - The formulae used are mostly taken from: - - Richard H. Rapp: Geometric Geodesy, Part I, (178 pp, 1991). - Columbus, Ohio: Dept. of Geodetic Science - and Surveying, Ohio State University. - -****************************************************************************************/ - - P->a = a; - P->es = es; - - /* Compute some ancillary ellipsoidal parameters */ - if (P->e==0) - P->e = sqrt(P->es); /* eccentricity */ - P->alpha = asin (P->e); /* angular eccentricity */ - - /* second eccentricity */ - P->e2 = tan (P->alpha); - P->e2s = P->e2 * P->e2; - - /* third eccentricity */ - P->e3 = (0!=P->alpha)? sin (P->alpha) / sqrt(2 - sin (P->alpha)*sin (P->alpha)): 0; - P->e3s = P->e3 * P->e3; - - /* flattening */ - if (0==P->f) - P->f = 1 - cos (P->alpha); /* = 1 - sqrt (1 - PIN->es); */ - P->rf = P->f != 0.0 ? 1.0/P->f: HUGE_VAL; - - /* second flattening */ - P->f2 = (cos(P->alpha)!=0)? 1/cos (P->alpha) - 1: 0; - P->rf2 = P->f2 != 0.0 ? 1/P->f2: HUGE_VAL; - - /* third flattening */ - P->n = pow (tan (P->alpha/2), 2); - P->rn = P->n != 0.0 ? 1/P->n: HUGE_VAL; - - /* ...and a few more */ - if (0==P->b) - P->b = (1 - P->f)*P->a; - P->rb = 1. / P->b; - P->ra = 1. / P->a; - - P->one_es = 1. - P->es; - if (P->one_es == 0.) { - pj_ctx_set_errno( P->ctx, PJD_ERR_ECCENTRICITY_IS_ONE); - return PJD_ERR_ECCENTRICITY_IS_ONE; - } - - P->rone_es = 1./P->one_es; - - return 0; -} - - - -#ifndef KEEP_ORIGINAL_PJ_ELL_SET -/**************************************************************************************/ -int pj_ell_set (PJ_CONTEXT *ctx, paralist *pl, double *a, double *es) { -/*************************************************************************************** - Initialize ellipsoidal parameters by emulating the original ellipsoid setup - function by Gerald Evenden, through a call to pj_ellipsoid -***************************************************************************************/ - PJ B; - int ret; - - memset (&B, 0, sizeof (B)); - B.ctx = ctx; - B.params = pl; - - ret = pj_ellipsoid (&B); - if (ret) - return ret; - - *a = B.a; - *es = B.es; - return 0; -} -#else - - -/**************************************************************************************/ -int pj_ell_set (projCtx ctx, paralist *pl, double *a, double *es) { -/*************************************************************************************** - Initialize ellipsoidal parameters: This is the original ellipsoid setup - function by Gerald Evenden - significantly more compact than pj_ellipsoid and - its many helper functions, and still quite readable. - - It is, however, also so tight that it is hard to modify and add functionality, - and equally hard to find the right place to add further commentary for improved - future maintainability. - - Hence, when the need to store in the PJ object, the parameters actually used to - define the ellipsoid came up, rather than modifying this little gem of - "economy of expression", a much more verbose reimplementation, pj_ellipsoid, - was written. -***************************************************************************************/ - int i; - double b=0.0, e; - char *name; - paralist *start = 0; - - /* clear any previous error */ - pj_ctx_set_errno(ctx,0); - - /* check for varying forms of ellipsoid input */ - *a = *es = 0.; - - /* R takes precedence */ - if (pj_param(ctx, pl, "tR").i) - *a = pj_param(ctx,pl, "dR").f; - - /* probable elliptical figure */ - else { - /* check if ellps present and temporarily append its values to pl */ - if ((name = pj_param(ctx,pl, "sellps").s) != NULL) { - char *s; - - for (start = pl; start && start->next ; start = start->next) ; - for (i = 0; (s = pj_ellps[i].id) && strcmp(name, s) ; ++i) ; - if (!s) { - pj_ctx_set_errno( ctx, PJD_ERR_UNKNOWN_ELLP_PARAM); - return 1; - } - start->next = pj_mkparam(pj_ellps[i].major); - start->next->next = pj_mkparam(pj_ellps[i].ell); - } - - *a = pj_param(ctx,pl, "da").f; - - if (pj_param(ctx,pl, "tes").i) /* eccentricity squared */ - *es = pj_param(ctx,pl, "des").f; - else if (pj_param(ctx,pl, "te").i) { /* eccentricity */ - e = pj_param(ctx,pl, "de").f; - *es = e * e; - } else if (pj_param(ctx,pl, "trf").i) { /* recip flattening */ - *es = pj_param(ctx,pl, "drf").f; - if (*es == 0.0) { - pj_ctx_set_errno(ctx, PJD_ERR_REV_FLATTENING_IS_ZERO); - goto bomb; - } - *es = 1./ *es; - *es = *es * (2. - *es); - } else if (pj_param(ctx,pl, "tf").i) { /* flattening */ - *es = pj_param(ctx,pl, "df").f; - *es = *es * (2. - *es); - } else if (pj_param(ctx,pl, "tb").i) { /* minor axis */ - b = pj_param(ctx,pl, "db").f; - *es = 1. - (b * b) / (*a * *a); - } /* else *es == 0. and sphere of radius *a */ - if (b == 0.0) - b = *a * sqrt(1. - *es); - - - /* following options turn ellipsoid into equivalent sphere */ - if (pj_param(ctx,pl, "bR_A").i) { /* sphere--area of ellipsoid */ - *a *= 1. - *es * (SIXTH + *es * (RA4 + *es * RA6)); - *es = 0.; - } else if (pj_param(ctx,pl, "bR_V").i) { /* sphere--vol. of ellipsoid */ - *a *= 1. - *es * (SIXTH + *es * (RV4 + *es * RV6)); - *es = 0.; - } else if (pj_param(ctx,pl, "bR_a").i) { /* sphere--arithmetic mean */ - *a = .5 * (*a + b); - *es = 0.; - } else if (pj_param(ctx,pl, "bR_g").i) { /* sphere--geometric mean */ - *a = sqrt(*a * b); - *es = 0.; - } else if (pj_param(ctx,pl, "bR_h").i) { /* sphere--harmonic mean */ - if ( (*a + b) == 0.0) { - pj_ctx_set_errno(ctx, PJD_ERR_TOLERANCE_CONDITION); - goto bomb; - } - *a = 2. * *a * b / (*a + b); - *es = 0.; - } else if ((i = pj_param(ctx,pl, "tR_lat_a").i) || /* sphere--arith. */ - pj_param(ctx,pl, "tR_lat_g").i) { /* or geom. mean at latitude */ - double tmp; - - tmp = sin(pj_param(ctx,pl, i ? "rR_lat_a" : "rR_lat_g").f); - if (fabs(tmp) > M_HALFPI) { - pj_ctx_set_errno(ctx, PJD_ERR_REF_RAD_LARGER_THAN_90); - goto bomb; - } - tmp = 1. - *es * tmp * tmp; - *a *= i ? .5 * (1. - *es + tmp) / ( tmp * sqrt(tmp)) : - sqrt(1. - *es) / tmp; - *es = 0.; - } -bomb: - if (start) { /* clean up temporary extension of list */ - pj_dalloc(start->next->next); - pj_dalloc(start->next); - start->next = 0; - } - if (ctx->last_errno) - return 1; - } - /* some remaining checks */ - if (*es < 0.) { - pj_ctx_set_errno(ctx, PJD_ERR_ES_LESS_THAN_ZERO); - return 1; - } - if (*a <= 0.) { - pj_ctx_set_errno(ctx, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); - return 1; - } - return 0; -} -#endif diff --git a/src/pj_ell_set.cpp b/src/pj_ell_set.cpp new file mode 100644 index 00000000..e2d2750c --- /dev/null +++ b/src/pj_ell_set.cpp @@ -0,0 +1,727 @@ +/* set ellipsoid parameters a and es */ + +#include +#include +#include + +#include "proj.h" +#include "proj_internal.h" +#include "projects.h" + + +/* Prototypes of the pj_ellipsoid helper functions */ +static int ellps_ellps (PJ *P); +static int ellps_size (PJ *P); +static int ellps_shape (PJ *P); +static int ellps_spherification (PJ *P); + +static paralist *pj_get_param (paralist *list, char *key); +static char *pj_param_value (paralist *list); +static const PJ_ELLPS *pj_find_ellps (char *name); + + +/***************************************************************************************/ +int pj_ellipsoid (PJ *P) { +/**************************************************************************************** + This is a replacement for the classic PROJ pj_ell_set function. The main difference + is that pj_ellipsoid augments the PJ object with a copy of the exact tags used to + define its related ellipsoid. + + This makes it possible to let a new PJ object inherit the geometrical properties + of an existing one. + + A complete ellipsoid definition comprises a size (primary) and a shape (secondary) + parameter. + + Size parameters supported are: + R, defining the radius of a spherical planet + a, defining the semimajor axis of an ellipsoidal planet + + Shape parameters supported are: + rf, the reverse flattening of the ellipsoid + f, the flattening of the ellipsoid + es, the eccentricity squared + e, the eccentricity + b, the semiminor axis + + The ellps=xxx parameter provides both size and shape for a number of built in + ellipsoid definitions. + + The ellipsoid definition may be augmented with a spherification flag, turning + the ellipsoid into a sphere with features defined by the ellipsoid. + + Spherification parameters supported are: + R_A, which gives a sphere with the same surface area as the ellipsoid + R_A, which gives a sphere with the same volume as the ellipsoid + + R_a, which gives a sphere with R = (a + b)/2 (arithmetic mean) + R_g, which gives a sphere with R = sqrt(a*b) (geometric mean) + R_h, which gives a sphere with R = 2*a*b/(a+b) (harmonic mean) + + R_lat_a=phi, which gives a sphere with R being the arithmetic mean of + of the corresponding ellipsoid at latitude phi. + R_lat_g=phi, which gives a sphere with R being the geometric mean of + of the corresponding ellipsoid at latitude phi. + + If R is given as size parameter, any shape and spherification parameters + given are ignored. + + If size and shape are given as ellps=xxx, later shape and size parameters + are are taken into account as modifiers for the built in ellipsoid definition. + + While this may seem strange, it is in accordance with historical PROJ + behaviour. It can e.g. be used to define coordinates on the ellipsoid + scaled to unit semimajor axis by specifying "+ellps=xxx +a=1" + +****************************************************************************************/ + int err = proj_errno_reset (P); + char *empty = {""}; + + P->def_size = P->def_shape = P->def_spherification = P->def_ellps = 0; + + /* Specifying R overrules everything */ + if (pj_get_param (P->params, "R")) { + ellps_size (P); + pj_calc_ellipsoid_params (P, P->a, 0); + if (proj_errno (P)) + return 1; + return proj_errno_restore (P, err); + } + + + /* If an ellps argument is specified, start by using that */ + if (0 != ellps_ellps (P)) + return 1; + + /* We may overwrite the size */ + if (0 != ellps_size (P)) + return 2; + + /* We may also overwrite the shape */ + if (0 != ellps_shape (P)) + return 3; + + /* When we're done with it, we compute all related ellipsoid parameters */ + pj_calc_ellipsoid_params (P, P->a, P->es); + + /* And finally, we may turn it into a sphere */ + if (0 != ellps_spherification (P)) + return 4; + + proj_log_debug (P, "pj_ellipsoid - final: a=%.3f f=1/%7.3f, errno=%d", + P->a, P->f!=0? 1/P->f: 0, proj_errno (P)); + proj_log_debug (P, "pj_ellipsoid - final: %s %s %s %s", + P->def_size? P->def_size: empty, + P->def_shape? P->def_shape: empty, + P->def_spherification? P->def_spherification: empty, + P->def_ellps? P->def_ellps: empty ); + + if (proj_errno (P)) + return 5; + + /* success */ + return proj_errno_restore (P, err); +} + + +/***************************************************************************************/ +static int ellps_ellps (PJ *P) { +/***************************************************************************************/ + PJ B; + const PJ_ELLPS *ellps; + paralist *par = 0; + char *name; + int err; + + /* Sail home if ellps=xxx is not specified */ + par = pj_get_param (P->params, "ellps"); + if (0==par) + return 0; + + /* Otherwise produce a fake PJ to make ellps_size/ellps_shape do the hard work for us */ + + /* First move B into P's context to get error messages onto the right channel */ + B.ctx = P->ctx; + + /* Then look up the right size and shape parameters from the builtin list */ + if (strlen (par->param) < 7) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + name = par->param + 6; + ellps = pj_find_ellps (name); + if (0==ellps) + return proj_errno_set (P, PJD_ERR_UNKNOWN_ELLP_PARAM); + + /* Now, get things ready for ellps_size/ellps_shape, make them do their thing, and clean up */ + err = proj_errno_reset (P); + B = *P; + pj_erase_ellipsoid_def (&B); + B.params = pj_mkparam (ellps->major); + B.params->next = pj_mkparam (ellps->ell); + + ellps_size (&B); + ellps_shape (&B); + + pj_dealloc (B.params->next); + pj_dealloc (B.params); + if (proj_errno (&B)) + return proj_errno (&B); + + /* Finally update P and sail home */ + pj_inherit_ellipsoid_def (&B, P); + P->def_ellps = par->param; + par->used = 1; + + return proj_errno_restore (P, err); +} + + +/***************************************************************************************/ +static int ellps_size (PJ *P) { +/***************************************************************************************/ + paralist *par = 0; + int a_was_set = 0; + + /* A size parameter *must* be given, but may have been given as ellps prior */ + if (P->a != 0) + a_was_set = 1; + + /* Check which size key is specified */ + par = pj_get_param (P->params, "R"); + if (0==par) + par = pj_get_param (P->params, "a"); + if (0==par) + return a_was_set? 0: proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); + + P->def_size = par->param; + par->used = 1; + P->a = pj_atof (pj_param_value (par)); + if (P->a <= 0) + return proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); + if (HUGE_VAL==P->a) + return proj_errno_set (P, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); + + if ('R'==par->param[0]) { + P->es = P->f = P->e = P->rf = 0; + P->b = P->a; + } + return 0; +} + + +/***************************************************************************************/ +static int ellps_shape (PJ *P) { +/***************************************************************************************/ + char *keys[] = {"rf", "f", "es", "e", "b"}; + paralist *par = 0; + char *def = 0; + size_t i, len; + + par = 0; + len = sizeof (keys) / sizeof (char *); + + /* Check which shape key is specified */ + for (i = 0; i < len; i++) { + par = pj_get_param (P->params, keys[i]); + if (par) + break; + } + + /* Not giving a shape parameter means selecting a sphere, unless shape */ + /* has been selected previously via ellps=xxx */ + if (0==par && P->es != 0) + return 0; + if (0==par && P->es==0) { + P->es = P->f = 0; + P->b = P->a; + return 0; + } + + P->def_shape = def = par->param; + par->used = 1; + P->es = P->f = P->b = P->e = P->rf = 0; + + switch (i) { + + /* reverse flattening, rf */ + case 0: + P->rf = pj_atof (pj_param_value (par)); + if (HUGE_VAL==P->rf) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + if (0==P->rf) + return proj_errno_set (P, PJD_ERR_REV_FLATTENING_IS_ZERO); + P->f = 1 / P->rf; + P->es = 2*P->f - P->f*P->f; + break; + + /* flattening, f */ + case 1: + P->f = pj_atof (pj_param_value (par)); + if (HUGE_VAL==P->f) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + + P->rf = P->f != 0.0 ? 1.0/P->f: HUGE_VAL; + P->es = 2*P->f - P->f*P->f; + break; + + /* eccentricity squared, es */ + case 2: + P->es = pj_atof (pj_param_value (par)); + if (HUGE_VAL==P->es) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + if (1==P->es) + return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); + break; + + /* eccentricity, e */ + case 3: + P->e = pj_atof (pj_param_value (par)); + if (HUGE_VAL==P->e) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + if (0==P->e) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + if (1==P->e) + return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); + P->es = P->e * P->e; + break; + + /* semiminor axis, b */ + case 4: + P->b = pj_atof (pj_param_value (par)); + if (HUGE_VAL==P->b) + return proj_errno_set (P, PJD_ERR_INVALID_ARG); + if (0==P->b) + return proj_errno_set (P, PJD_ERR_ECCENTRICITY_IS_ONE); + if (P->b==P->a) + break; + P->f = (P->a - P->b) / P->a; + P->es = 2*P->f - P->f*P->f; + break; + default: + return PJD_ERR_INVALID_ARG; + + } + + if (P->es < 0) + return proj_errno_set (P, PJD_ERR_ES_LESS_THAN_ZERO); + return 0; +} + + +/* series coefficients for calculating ellipsoid-equivalent spheres */ +static const double SIXTH = 1/6.; +static const double RA4 = 17/360.; +static const double RA6 = 67/3024.; +static const double RV4 = 5/72.; +static const double RV6 = 55/1296.; + +/***************************************************************************************/ +static int ellps_spherification (PJ *P) { +/***************************************************************************************/ + char *keys[] = {"R_A", "R_V", "R_a", "R_g", "R_h", "R_lat_a", "R_lat_g"}; + size_t len, i; + paralist *par = 0; + char *def = 0; + + double t; + char *v, *endp; + + len = sizeof (keys) / sizeof (char *); + P->def_spherification = 0; + + /* Check which spherification key is specified */ + for (i = 0; i < len; i++) { + par = pj_get_param (P->params, keys[i]); + if (par) + break; + } + + /* No spherification specified? Then we're done */ + if (i==len) + return 0; + + /* Store definition */ + P->def_spherification = def = par->param; + par->used = 1; + + switch (i) { + + /* R_A - a sphere with same area as ellipsoid */ + case 0: + P->a *= 1. - P->es * (SIXTH + P->es * (RA4 + P->es * RA6)); + break; + + /* R_V - a sphere with same volume as ellipsoid */ + case 1: + P->a *= 1. - P->es * (SIXTH + P->es * (RV4 + P->es * RV6)); + break; + + /* R_a - a sphere with R = the arithmetic mean of the ellipsoid */ + case 2: + P->a = (P->a + P->b) / 2; + break; + + /* R_g - a sphere with R = the geometric mean of the ellipsoid */ + case 3: + P->a = sqrt (P->a * P->b); + break; + + /* R_h - a sphere with R = the harmonic mean of the ellipsoid */ + case 4: + if (P->a + P->b == 0) + return proj_errno_set (P, PJD_ERR_TOLERANCE_CONDITION); + P->a = (2*P->a * P->b) / (P->a + P->b); + break; + + /* R_lat_a - a sphere with R = the arithmetic mean of the ellipsoid at given latitude */ + case 5: + /* R_lat_g - a sphere with R = the geometric mean of the ellipsoid at given latitude */ + case 6: + v = pj_param_value (par); + t = proj_dmstor (v, &endp); + if (fabs (t) > M_HALFPI) + return proj_errno_set (P, PJD_ERR_REF_RAD_LARGER_THAN_90); + t = sin (t); + t = 1 - P->es * t * t; + if (i==5) /* arithmetic */ + P->a *= (1. - P->es + t) / (2 * t * sqrt(t)); + else /* geometric */ + P->a *= sqrt (1 - P->es) / t; + break; + } + + /* Clean up the ellipsoidal parameters to reflect the sphere */ + P->es = P->e = P->f = 0; + P->rf = HUGE_VAL; + P->b = P->a; + pj_calc_ellipsoid_params (P, P->a, 0); + + return 0; +} + + +/* locate parameter in list */ +static paralist *pj_get_param (paralist *list, char *key) { + size_t l = strlen(key); + while (list && !(0==strncmp(list->param, key, l) && (0==list->param[l] || list->param[l] == '=') ) ) + list = list->next; + return list; +} + + +static char *pj_param_value (paralist *list) { + char *key, *value; + if (0==list) + return 0; + + key = list->param; + value = strchr (key, '='); + + /* a flag (i.e. a key without value) has its own name (key) as value */ + return value? value + 1: key; +} + + +static const PJ_ELLPS *pj_find_ellps (char *name) { + int i; + const char *s; + const PJ_ELLPS *ellps; + + if (0==name) + return 0; + + ellps = proj_list_ellps(); + + /* Search through internal ellipsoid list for name */ + for (i = 0; (s = ellps[i].id) && strcmp(name, s) ; ++i); + if (0==s) + return 0; + return ellps + i; +} + + +/**************************************************************************************/ +void pj_erase_ellipsoid_def (PJ *P) { +/*************************************************************************************** + Erase all ellipsoidal parameters in P +***************************************************************************************/ + PJ B; + + /* Make a blank PJ to copy from */ + memset (&B, 0, sizeof (B)); + + /* And use it to overwrite all existing ellipsoid defs */ + pj_inherit_ellipsoid_def (&B, P); +} + + +/**************************************************************************************/ +void pj_inherit_ellipsoid_def (const PJ *src, PJ *dst) { +/*************************************************************************************** + Brute force copy the ellipsoidal parameters from src to dst. This code was + written before the actual ellipsoid setup parameters were kept available in + the PJ->def_xxx elements. +***************************************************************************************/ + + /* The linear parameters */ + dst->a = src->a; + dst->b = src->b; + dst->ra = src->ra; + dst->rb = src->rb; + + /* The eccentricities */ + dst->alpha = src->alpha; + dst->e = src->e; + dst->es = src->es; + dst->e2 = src->e2; + dst->e2s = src->e2s; + dst->e3 = src->e3; + dst->e3s = src->e3s; + dst->one_es = src->one_es; + dst->rone_es = src->rone_es; + + /* The flattenings */ + dst->f = src->f; + dst->f2 = src->f2; + dst->n = src->n; + dst->rf = src->rf; + dst->rf2 = src->rf2; + dst->rn = src->rn; + + /* This one's for GRS80 */ + dst->J = src->J; + + /* es and a before any +proj related adjustment */ + dst->es_orig = src->es_orig; + dst->a_orig = src->a_orig; +} + + +/***************************************************************************************/ +int pj_calc_ellipsoid_params (PJ *P, double a, double es) { +/**************************************************************************************** + Calculate a large number of ancillary ellipsoidal parameters, in addition to + the two traditional PROJ defining parameters: Semimajor axis, a, and the + eccentricity squared, es. + + Most of these parameters are fairly cheap to compute in comparison to the overall + effort involved in initializing a PJ object. They may, however, take a substantial + part of the time taken in computing an individual point transformation. + + So by providing them up front, we can amortize the (already modest) cost over all + transformations carried out over the entire lifetime of a PJ object, rather than + incur that cost for every single transformation. + + Most of the parameter calculations here are based on the "angular eccentricity", + i.e. the angle, measured from the semiminor axis, of a line going from the north + pole to one of the foci of the ellipsoid - or in other words: The arc sine of the + eccentricity. + + The formulae used are mostly taken from: + + Richard H. Rapp: Geometric Geodesy, Part I, (178 pp, 1991). + Columbus, Ohio: Dept. of Geodetic Science + and Surveying, Ohio State University. + +****************************************************************************************/ + + P->a = a; + P->es = es; + + /* Compute some ancillary ellipsoidal parameters */ + if (P->e==0) + P->e = sqrt(P->es); /* eccentricity */ + P->alpha = asin (P->e); /* angular eccentricity */ + + /* second eccentricity */ + P->e2 = tan (P->alpha); + P->e2s = P->e2 * P->e2; + + /* third eccentricity */ + P->e3 = (0!=P->alpha)? sin (P->alpha) / sqrt(2 - sin (P->alpha)*sin (P->alpha)): 0; + P->e3s = P->e3 * P->e3; + + /* flattening */ + if (0==P->f) + P->f = 1 - cos (P->alpha); /* = 1 - sqrt (1 - PIN->es); */ + P->rf = P->f != 0.0 ? 1.0/P->f: HUGE_VAL; + + /* second flattening */ + P->f2 = (cos(P->alpha)!=0)? 1/cos (P->alpha) - 1: 0; + P->rf2 = P->f2 != 0.0 ? 1/P->f2: HUGE_VAL; + + /* third flattening */ + P->n = pow (tan (P->alpha/2), 2); + P->rn = P->n != 0.0 ? 1/P->n: HUGE_VAL; + + /* ...and a few more */ + if (0==P->b) + P->b = (1 - P->f)*P->a; + P->rb = 1. / P->b; + P->ra = 1. / P->a; + + P->one_es = 1. - P->es; + if (P->one_es == 0.) { + pj_ctx_set_errno( P->ctx, PJD_ERR_ECCENTRICITY_IS_ONE); + return PJD_ERR_ECCENTRICITY_IS_ONE; + } + + P->rone_es = 1./P->one_es; + + return 0; +} + + + +#ifndef KEEP_ORIGINAL_PJ_ELL_SET +/**************************************************************************************/ +int pj_ell_set (PJ_CONTEXT *ctx, paralist *pl, double *a, double *es) { +/*************************************************************************************** + Initialize ellipsoidal parameters by emulating the original ellipsoid setup + function by Gerald Evenden, through a call to pj_ellipsoid +***************************************************************************************/ + PJ B; + int ret; + + memset (&B, 0, sizeof (B)); + B.ctx = ctx; + B.params = pl; + + ret = pj_ellipsoid (&B); + if (ret) + return ret; + + *a = B.a; + *es = B.es; + return 0; +} +#else + + +/**************************************************************************************/ +int pj_ell_set (projCtx ctx, paralist *pl, double *a, double *es) { +/*************************************************************************************** + Initialize ellipsoidal parameters: This is the original ellipsoid setup + function by Gerald Evenden - significantly more compact than pj_ellipsoid and + its many helper functions, and still quite readable. + + It is, however, also so tight that it is hard to modify and add functionality, + and equally hard to find the right place to add further commentary for improved + future maintainability. + + Hence, when the need to store in the PJ object, the parameters actually used to + define the ellipsoid came up, rather than modifying this little gem of + "economy of expression", a much more verbose reimplementation, pj_ellipsoid, + was written. +***************************************************************************************/ + int i; + double b=0.0, e; + char *name; + paralist *start = 0; + + /* clear any previous error */ + pj_ctx_set_errno(ctx,0); + + /* check for varying forms of ellipsoid input */ + *a = *es = 0.; + + /* R takes precedence */ + if (pj_param(ctx, pl, "tR").i) + *a = pj_param(ctx,pl, "dR").f; + + /* probable elliptical figure */ + else { + /* check if ellps present and temporarily append its values to pl */ + if ((name = pj_param(ctx,pl, "sellps").s) != NULL) { + char *s; + + for (start = pl; start && start->next ; start = start->next) ; + for (i = 0; (s = pj_ellps[i].id) && strcmp(name, s) ; ++i) ; + if (!s) { + pj_ctx_set_errno( ctx, PJD_ERR_UNKNOWN_ELLP_PARAM); + return 1; + } + start->next = pj_mkparam(pj_ellps[i].major); + start->next->next = pj_mkparam(pj_ellps[i].ell); + } + + *a = pj_param(ctx,pl, "da").f; + + if (pj_param(ctx,pl, "tes").i) /* eccentricity squared */ + *es = pj_param(ctx,pl, "des").f; + else if (pj_param(ctx,pl, "te").i) { /* eccentricity */ + e = pj_param(ctx,pl, "de").f; + *es = e * e; + } else if (pj_param(ctx,pl, "trf").i) { /* recip flattening */ + *es = pj_param(ctx,pl, "drf").f; + if (*es == 0.0) { + pj_ctx_set_errno(ctx, PJD_ERR_REV_FLATTENING_IS_ZERO); + goto bomb; + } + *es = 1./ *es; + *es = *es * (2. - *es); + } else if (pj_param(ctx,pl, "tf").i) { /* flattening */ + *es = pj_param(ctx,pl, "df").f; + *es = *es * (2. - *es); + } else if (pj_param(ctx,pl, "tb").i) { /* minor axis */ + b = pj_param(ctx,pl, "db").f; + *es = 1. - (b * b) / (*a * *a); + } /* else *es == 0. and sphere of radius *a */ + if (b == 0.0) + b = *a * sqrt(1. - *es); + + + /* following options turn ellipsoid into equivalent sphere */ + if (pj_param(ctx,pl, "bR_A").i) { /* sphere--area of ellipsoid */ + *a *= 1. - *es * (SIXTH + *es * (RA4 + *es * RA6)); + *es = 0.; + } else if (pj_param(ctx,pl, "bR_V").i) { /* sphere--vol. of ellipsoid */ + *a *= 1. - *es * (SIXTH + *es * (RV4 + *es * RV6)); + *es = 0.; + } else if (pj_param(ctx,pl, "bR_a").i) { /* sphere--arithmetic mean */ + *a = .5 * (*a + b); + *es = 0.; + } else if (pj_param(ctx,pl, "bR_g").i) { /* sphere--geometric mean */ + *a = sqrt(*a * b); + *es = 0.; + } else if (pj_param(ctx,pl, "bR_h").i) { /* sphere--harmonic mean */ + if ( (*a + b) == 0.0) { + pj_ctx_set_errno(ctx, PJD_ERR_TOLERANCE_CONDITION); + goto bomb; + } + *a = 2. * *a * b / (*a + b); + *es = 0.; + } else if ((i = pj_param(ctx,pl, "tR_lat_a").i) || /* sphere--arith. */ + pj_param(ctx,pl, "tR_lat_g").i) { /* or geom. mean at latitude */ + double tmp; + + tmp = sin(pj_param(ctx,pl, i ? "rR_lat_a" : "rR_lat_g").f); + if (fabs(tmp) > M_HALFPI) { + pj_ctx_set_errno(ctx, PJD_ERR_REF_RAD_LARGER_THAN_90); + goto bomb; + } + tmp = 1. - *es * tmp * tmp; + *a *= i ? .5 * (1. - *es + tmp) / ( tmp * sqrt(tmp)) : + sqrt(1. - *es) / tmp; + *es = 0.; + } +bomb: + if (start) { /* clean up temporary extension of list */ + pj_dalloc(start->next->next); + pj_dalloc(start->next); + start->next = 0; + } + if (ctx->last_errno) + return 1; + } + /* some remaining checks */ + if (*es < 0.) { + pj_ctx_set_errno(ctx, PJD_ERR_ES_LESS_THAN_ZERO); + return 1; + } + if (*a <= 0.) { + pj_ctx_set_errno(ctx, PJD_ERR_MAJOR_AXIS_NOT_GIVEN); + return 1; + } + return 0; +} +#endif diff --git a/src/pj_ellps.c b/src/pj_ellps.c deleted file mode 100644 index 8b3b8f0a..00000000 --- a/src/pj_ellps.c +++ /dev/null @@ -1,62 +0,0 @@ -/* definition of standard geoids */ - -#include - -#include "proj.h" -#include "projects.h" - -static const struct PJ_ELLPS -pj_ellps[] = { -{"MERIT", "a=6378137.0", "rf=298.257", "MERIT 1983"}, -{"SGS85", "a=6378136.0", "rf=298.257", "Soviet Geodetic System 85"}, -{"GRS80", "a=6378137.0", "rf=298.257222101", "GRS 1980(IUGG, 1980)"}, -{"IAU76", "a=6378140.0", "rf=298.257", "IAU 1976"}, -{"airy", "a=6377563.396", "rf=299.3249646", "Airy 1830"}, -{"APL4.9", "a=6378137.0", "rf=298.25", "Appl. Physics. 1965"}, -{"NWL9D", "a=6378145.0", "rf=298.25", "Naval Weapons Lab., 1965"}, -{"mod_airy", "a=6377340.189", "b=6356034.446", "Modified Airy"}, -{"andrae", "a=6377104.43", "rf=300.0", "Andrae 1876 (Den., Iclnd.)"}, -{"danish", "a=6377019.2563", "rf=300.0", "Andrae 1876 (Denmark, Iceland)"}, -{"aust_SA", "a=6378160.0", "rf=298.25", "Australian Natl & S. Amer. 1969"}, -{"GRS67", "a=6378160.0", "rf=298.2471674270", "GRS 67(IUGG 1967)"}, -{"GSK2011", "a=6378136.5", "rf=298.2564151", "GSK-2011"}, -{"bessel", "a=6377397.155", "rf=299.1528128", "Bessel 1841"}, -{"bess_nam", "a=6377483.865", "rf=299.1528128", "Bessel 1841 (Namibia)"}, -{"clrk66", "a=6378206.4", "b=6356583.8", "Clarke 1866"}, -{"clrk80", "a=6378249.145", "rf=293.4663", "Clarke 1880 mod."}, -{"clrk80ign", "a=6378249.2", "rf=293.4660212936269", "Clarke 1880 (IGN)."}, -{"CPM", "a=6375738.7", "rf=334.29", "Comm. des Poids et Mesures 1799"}, -{"delmbr", "a=6376428.", "rf=311.5", "Delambre 1810 (Belgium)"}, -{"engelis", "a=6378136.05", "rf=298.2566", "Engelis 1985"}, -{"evrst30", "a=6377276.345", "rf=300.8017", "Everest 1830"}, -{"evrst48", "a=6377304.063", "rf=300.8017", "Everest 1948"}, -{"evrst56", "a=6377301.243", "rf=300.8017", "Everest 1956"}, -{"evrst69", "a=6377295.664", "rf=300.8017", "Everest 1969"}, -{"evrstSS", "a=6377298.556", "rf=300.8017", "Everest (Sabah & Sarawak)"}, -{"fschr60", "a=6378166.", "rf=298.3", "Fischer (Mercury Datum) 1960"}, -{"fschr60m", "a=6378155.", "rf=298.3", "Modified Fischer 1960"}, -{"fschr68", "a=6378150.", "rf=298.3", "Fischer 1968"}, -{"helmert", "a=6378200.", "rf=298.3", "Helmert 1906"}, -{"hough", "a=6378270.0", "rf=297.", "Hough"}, -{"intl", "a=6378388.0", "rf=297.", "International 1909 (Hayford)"}, -{"krass", "a=6378245.0", "rf=298.3", "Krassovsky, 1942"}, -{"kaula", "a=6378163.", "rf=298.24", "Kaula 1961"}, -{"lerch", "a=6378139.", "rf=298.257", "Lerch 1979"}, -{"mprts", "a=6397300.", "rf=191.", "Maupertius 1738"}, -{"new_intl", "a=6378157.5", "b=6356772.2", "New International 1967"}, -{"plessis", "a=6376523.", "b=6355863.", "Plessis 1817 (France)"}, -{"PZ90", "a=6378136.0", "rf=298.25784", "PZ-90"}, -{"SEasia", "a=6378155.0", "b=6356773.3205", "Southeast Asia"}, -{"walbeck", "a=6376896.0", "b=6355834.8467", "Walbeck"}, -{"WGS60", "a=6378165.0", "rf=298.3", "WGS 60"}, -{"WGS66", "a=6378145.0", "rf=298.25", "WGS 66"}, -{"WGS72", "a=6378135.0", "rf=298.26", "WGS 72"}, -{"WGS84", "a=6378137.0", "rf=298.257223563", "WGS 84"}, -{"sphere", "a=6370997.0", "b=6370997.0", "Normal Sphere (r=6370997)"}, -{NULL, NULL, NULL, NULL} -}; - -const PJ_ELLPS *proj_list_ellps(void) -{ - return pj_ellps; -} diff --git a/src/pj_ellps.cpp b/src/pj_ellps.cpp new file mode 100644 index 00000000..8b3b8f0a --- /dev/null +++ b/src/pj_ellps.cpp @@ -0,0 +1,62 @@ +/* definition of standard geoids */ + +#include + +#include "proj.h" +#include "projects.h" + +static const struct PJ_ELLPS +pj_ellps[] = { +{"MERIT", "a=6378137.0", "rf=298.257", "MERIT 1983"}, +{"SGS85", "a=6378136.0", "rf=298.257", "Soviet Geodetic System 85"}, +{"GRS80", "a=6378137.0", "rf=298.257222101", "GRS 1980(IUGG, 1980)"}, +{"IAU76", "a=6378140.0", "rf=298.257", "IAU 1976"}, +{"airy", "a=6377563.396", "rf=299.3249646", "Airy 1830"}, +{"APL4.9", "a=6378137.0", "rf=298.25", "Appl. Physics. 1965"}, +{"NWL9D", "a=6378145.0", "rf=298.25", "Naval Weapons Lab., 1965"}, +{"mod_airy", "a=6377340.189", "b=6356034.446", "Modified Airy"}, +{"andrae", "a=6377104.43", "rf=300.0", "Andrae 1876 (Den., Iclnd.)"}, +{"danish", "a=6377019.2563", "rf=300.0", "Andrae 1876 (Denmark, Iceland)"}, +{"aust_SA", "a=6378160.0", "rf=298.25", "Australian Natl & S. Amer. 1969"}, +{"GRS67", "a=6378160.0", "rf=298.2471674270", "GRS 67(IUGG 1967)"}, +{"GSK2011", "a=6378136.5", "rf=298.2564151", "GSK-2011"}, +{"bessel", "a=6377397.155", "rf=299.1528128", "Bessel 1841"}, +{"bess_nam", "a=6377483.865", "rf=299.1528128", "Bessel 1841 (Namibia)"}, +{"clrk66", "a=6378206.4", "b=6356583.8", "Clarke 1866"}, +{"clrk80", "a=6378249.145", "rf=293.4663", "Clarke 1880 mod."}, +{"clrk80ign", "a=6378249.2", "rf=293.4660212936269", "Clarke 1880 (IGN)."}, +{"CPM", "a=6375738.7", "rf=334.29", "Comm. des Poids et Mesures 1799"}, +{"delmbr", "a=6376428.", "rf=311.5", "Delambre 1810 (Belgium)"}, +{"engelis", "a=6378136.05", "rf=298.2566", "Engelis 1985"}, +{"evrst30", "a=6377276.345", "rf=300.8017", "Everest 1830"}, +{"evrst48", "a=6377304.063", "rf=300.8017", "Everest 1948"}, +{"evrst56", "a=6377301.243", "rf=300.8017", "Everest 1956"}, +{"evrst69", "a=6377295.664", "rf=300.8017", "Everest 1969"}, +{"evrstSS", "a=6377298.556", "rf=300.8017", "Everest (Sabah & Sarawak)"}, +{"fschr60", "a=6378166.", "rf=298.3", "Fischer (Mercury Datum) 1960"}, +{"fschr60m", "a=6378155.", "rf=298.3", "Modified Fischer 1960"}, +{"fschr68", "a=6378150.", "rf=298.3", "Fischer 1968"}, +{"helmert", "a=6378200.", "rf=298.3", "Helmert 1906"}, +{"hough", "a=6378270.0", "rf=297.", "Hough"}, +{"intl", "a=6378388.0", "rf=297.", "International 1909 (Hayford)"}, +{"krass", "a=6378245.0", "rf=298.3", "Krassovsky, 1942"}, +{"kaula", "a=6378163.", "rf=298.24", "Kaula 1961"}, +{"lerch", "a=6378139.", "rf=298.257", "Lerch 1979"}, +{"mprts", "a=6397300.", "rf=191.", "Maupertius 1738"}, +{"new_intl", "a=6378157.5", "b=6356772.2", "New International 1967"}, +{"plessis", "a=6376523.", "b=6355863.", "Plessis 1817 (France)"}, +{"PZ90", "a=6378136.0", "rf=298.25784", "PZ-90"}, +{"SEasia", "a=6378155.0", "b=6356773.3205", "Southeast Asia"}, +{"walbeck", "a=6376896.0", "b=6355834.8467", "Walbeck"}, +{"WGS60", "a=6378165.0", "rf=298.3", "WGS 60"}, +{"WGS66", "a=6378145.0", "rf=298.25", "WGS 66"}, +{"WGS72", "a=6378135.0", "rf=298.26", "WGS 72"}, +{"WGS84", "a=6378137.0", "rf=298.257223563", "WGS 84"}, +{"sphere", "a=6370997.0", "b=6370997.0", "Normal Sphere (r=6370997)"}, +{NULL, NULL, NULL, NULL} +}; + +const PJ_ELLPS *proj_list_ellps(void) +{ + return pj_ellps; +} diff --git a/src/pj_errno.c b/src/pj_errno.c deleted file mode 100644 index 6e98cd73..00000000 --- a/src/pj_errno.c +++ /dev/null @@ -1,17 +0,0 @@ -/* For full ANSI compliance of global variable */ - -#include "projects.h" - -C_NAMESPACE_VAR int pj_errno = 0; - -/************************************************************************/ -/* pj_get_errno_ref() */ -/************************************************************************/ - -int *pj_get_errno_ref() - -{ - return &pj_errno; -} - -/* end */ diff --git a/src/pj_errno.cpp b/src/pj_errno.cpp new file mode 100644 index 00000000..6e98cd73 --- /dev/null +++ b/src/pj_errno.cpp @@ -0,0 +1,17 @@ +/* For full ANSI compliance of global variable */ + +#include "projects.h" + +C_NAMESPACE_VAR int pj_errno = 0; + +/************************************************************************/ +/* pj_get_errno_ref() */ +/************************************************************************/ + +int *pj_get_errno_ref() + +{ + return &pj_errno; +} + +/* end */ diff --git a/src/pj_factors.c b/src/pj_factors.c deleted file mode 100644 index e4b871a1..00000000 --- a/src/pj_factors.c +++ /dev/null @@ -1,107 +0,0 @@ -/* projection scale factors */ -#define PJ_LIB__ -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -#include - -#ifndef DEFAULT_H -#define DEFAULT_H 1e-5 /* radian default for numeric h */ -#endif - -#define EPS 1.0e-12 - -int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) { - double cosphi, t, n, r; - int err; - PJ_COORD coo = {{0, 0, 0, 0}}; - coo.lp = lp; - - /* Failing the 3 initial checks will most likely be due to */ - /* earlier errors, so we leave errno alone */ - if (0==fac) - return 1; - - if (0==P) - return 1; - - if (HUGE_VAL==lp.lam) - return 1; - - /* But from here, we're ready to make our own mistakes */ - err = proj_errno_reset (P); - - /* Indicate that all factors are numerical approximations */ - fac->code = 0; - - /* Check for latitude or longitude overange */ - if ((fabs (lp.phi)-M_HALFPI) > EPS || fabs (lp.lam) > 10.) { - proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - return 1; - } - - /* Set a reasonable step size for the numerical derivatives */ - h = fabs (h); - if (h < EPS) - h = DEFAULT_H; - - /* If input latitudes are geocentric, convert to geographic */ - if (P->geoc) - lp = pj_geocentric_latitude (P, PJ_INV, coo).lp; - - /* If latitude + one step overshoots the pole, move it slightly inside, */ - /* so the numerical derivative still exists */ - if (fabs (lp.phi) > (M_HALFPI - h)) - lp.phi = lp.phi < 0. ? -(M_HALFPI-h) : (M_HALFPI-h); - - /* Longitudinal distance from central meridian */ - lp.lam -= P->lam0; - if (!P->over) - lp.lam = adjlon(lp.lam); - - /* Derivatives */ - if (pj_deriv (lp, h, P, &(fac->der))) { - proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - return 1; - } - - /* Scale factors */ - cosphi = cos (lp.phi); - fac->h = hypot (fac->der.x_p, fac->der.y_p); - fac->k = hypot (fac->der.x_l, fac->der.y_l) / cosphi; - - if (P->es != 0.0) { - t = sin(lp.phi); - t = 1. - P->es * t * t; - n = sqrt(t); - fac->h *= t * n / P->one_es; - fac->k *= n; - r = t * t / P->one_es; - } else - r = 1.; - - /* Convergence */ - fac->conv = -atan2 (fac->der.x_p, fac->der.y_p); - - /* Areal scale factor */ - fac->s = (fac->der.y_p * fac->der.x_l - fac->der.x_p * fac->der.y_l) * r / cosphi; - - /* Meridian-parallel angle (theta prime) */ - fac->thetap = aasin(P->ctx,fac->s / (fac->h * fac->k)); - - /* Tissot ellipse axis */ - t = fac->k * fac->k + fac->h * fac->h; - fac->a = sqrt(t + 2. * fac->s); - t = t - 2. * fac->s; - t = t > 0? sqrt(t): 0; - fac->b = 0.5 * (fac->a - t); - fac->a = 0.5 * (fac->a + t); - - /* Angular distortion */ - fac->omega = 2. * aasin(P->ctx, (fac->a - fac->b) / (fac->a + fac->b) ); - - proj_errno_restore (P, err); - return 0; -} diff --git a/src/pj_factors.cpp b/src/pj_factors.cpp new file mode 100644 index 00000000..e4b871a1 --- /dev/null +++ b/src/pj_factors.cpp @@ -0,0 +1,107 @@ +/* projection scale factors */ +#define PJ_LIB__ +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +#include + +#ifndef DEFAULT_H +#define DEFAULT_H 1e-5 /* radian default for numeric h */ +#endif + +#define EPS 1.0e-12 + +int pj_factors(LP lp, const PJ *P, double h, struct FACTORS *fac) { + double cosphi, t, n, r; + int err; + PJ_COORD coo = {{0, 0, 0, 0}}; + coo.lp = lp; + + /* Failing the 3 initial checks will most likely be due to */ + /* earlier errors, so we leave errno alone */ + if (0==fac) + return 1; + + if (0==P) + return 1; + + if (HUGE_VAL==lp.lam) + return 1; + + /* But from here, we're ready to make our own mistakes */ + err = proj_errno_reset (P); + + /* Indicate that all factors are numerical approximations */ + fac->code = 0; + + /* Check for latitude or longitude overange */ + if ((fabs (lp.phi)-M_HALFPI) > EPS || fabs (lp.lam) > 10.) { + proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + return 1; + } + + /* Set a reasonable step size for the numerical derivatives */ + h = fabs (h); + if (h < EPS) + h = DEFAULT_H; + + /* If input latitudes are geocentric, convert to geographic */ + if (P->geoc) + lp = pj_geocentric_latitude (P, PJ_INV, coo).lp; + + /* If latitude + one step overshoots the pole, move it slightly inside, */ + /* so the numerical derivative still exists */ + if (fabs (lp.phi) > (M_HALFPI - h)) + lp.phi = lp.phi < 0. ? -(M_HALFPI-h) : (M_HALFPI-h); + + /* Longitudinal distance from central meridian */ + lp.lam -= P->lam0; + if (!P->over) + lp.lam = adjlon(lp.lam); + + /* Derivatives */ + if (pj_deriv (lp, h, P, &(fac->der))) { + proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + return 1; + } + + /* Scale factors */ + cosphi = cos (lp.phi); + fac->h = hypot (fac->der.x_p, fac->der.y_p); + fac->k = hypot (fac->der.x_l, fac->der.y_l) / cosphi; + + if (P->es != 0.0) { + t = sin(lp.phi); + t = 1. - P->es * t * t; + n = sqrt(t); + fac->h *= t * n / P->one_es; + fac->k *= n; + r = t * t / P->one_es; + } else + r = 1.; + + /* Convergence */ + fac->conv = -atan2 (fac->der.x_p, fac->der.y_p); + + /* Areal scale factor */ + fac->s = (fac->der.y_p * fac->der.x_l - fac->der.x_p * fac->der.y_l) * r / cosphi; + + /* Meridian-parallel angle (theta prime) */ + fac->thetap = aasin(P->ctx,fac->s / (fac->h * fac->k)); + + /* Tissot ellipse axis */ + t = fac->k * fac->k + fac->h * fac->h; + fac->a = sqrt(t + 2. * fac->s); + t = t - 2. * fac->s; + t = t > 0? sqrt(t): 0; + fac->b = 0.5 * (fac->a - t); + fac->a = 0.5 * (fac->a + t); + + /* Angular distortion */ + fac->omega = 2. * aasin(P->ctx, (fac->a - fac->b) / (fac->a + fac->b) ); + + proj_errno_restore (P, err); + return 0; +} diff --git a/src/pj_fileapi.c b/src/pj_fileapi.c deleted file mode 100644 index eba96afd..00000000 --- a/src/pj_fileapi.c +++ /dev/null @@ -1,213 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of the pj_ctx_* file api, and the default stdio - * based implementation. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2013, Frank Warmerdam - * - * 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 -#include -#include -#include -#include - -#include "projects.h" - -static PAFile stdio_fopen(projCtx ctx, const char *filename, - const char *access); -static size_t stdio_fread(void *buffer, size_t size, size_t nmemb, - PAFile file); -static int stdio_fseek(PAFile file, long offset, int whence); -static long stdio_ftell(PAFile file); -static void stdio_fclose(PAFile file); - -static projFileAPI default_fileapi = { - stdio_fopen, - stdio_fread, - stdio_fseek, - stdio_ftell, - stdio_fclose -}; - -typedef struct { - projCtx ctx; - FILE *fp; -} stdio_pafile; - -/************************************************************************/ -/* pj_get_default_fileapi() */ -/************************************************************************/ - -projFileAPI *pj_get_default_fileapi(void) -{ - return &default_fileapi; -} - -/************************************************************************/ -/* stdio_fopen() */ -/************************************************************************/ - -static PAFile stdio_fopen(projCtx ctx, const char *filename, - const char *access) -{ - stdio_pafile *pafile; - FILE *fp; - - fp = fopen(filename, access); - if (fp == NULL) - { - return NULL; - } - - pafile = (stdio_pafile *) malloc(sizeof(stdio_pafile)); - if (!pafile) - { - pj_ctx_set_errno(ctx, ENOMEM); - fclose(fp); - return NULL; - } - - pafile->fp = fp; - pafile->ctx = ctx; - return (PAFile) pafile; -} - -/************************************************************************/ -/* stdio_fread() */ -/************************************************************************/ - -static size_t stdio_fread(void *buffer, size_t size, size_t nmemb, - PAFile file) -{ - stdio_pafile *pafile = (stdio_pafile *) file; - return fread(buffer, size, nmemb, pafile->fp); -} - -/************************************************************************/ -/* stdio_fseek() */ -/************************************************************************/ -static int stdio_fseek(PAFile file, long offset, int whence) -{ - stdio_pafile *pafile = (stdio_pafile *) file; - return fseek(pafile->fp, offset, whence); -} - -/************************************************************************/ -/* stdio_ftell() */ -/************************************************************************/ -static long stdio_ftell(PAFile file) -{ - stdio_pafile *pafile = (stdio_pafile *) file; - return ftell(pafile->fp); -} - -/************************************************************************/ -/* stdio_fclose() */ -/************************************************************************/ -static void stdio_fclose(PAFile file) -{ - stdio_pafile *pafile = (stdio_pafile *) file; - fclose(pafile->fp); - free(pafile); -} - -/************************************************************************/ -/* pj_ctx_fopen() */ -/* */ -/* Open a file using the provided file io hooks. */ -/************************************************************************/ - -PAFile pj_ctx_fopen(projCtx ctx, const char *filename, const char *access) -{ - return ctx->fileapi->FOpen(ctx, filename, access); -} - -/************************************************************************/ -/* pj_ctx_fread() */ -/************************************************************************/ -size_t pj_ctx_fread(projCtx ctx, void *buffer, size_t size, size_t nmemb, PAFile file) -{ - return ctx->fileapi->FRead(buffer, size, nmemb, file); -} - -/************************************************************************/ -/* pj_ctx_fseek() */ -/************************************************************************/ -int pj_ctx_fseek(projCtx ctx, PAFile file, long offset, int whence) -{ - return ctx->fileapi->FSeek(file, offset, whence); -} - -/************************************************************************/ -/* pj_ctx_ftell() */ -/************************************************************************/ -long pj_ctx_ftell(projCtx ctx, PAFile file) -{ - return ctx->fileapi->FTell(file); -} - -/************************************************************************/ -/* pj_ctx_fclose() */ -/************************************************************************/ -void pj_ctx_fclose(projCtx ctx, PAFile file) -{ - ctx->fileapi->FClose(file); -} - -/************************************************************************/ -/* pj_ctx_fgets() */ -/* */ -/* A not very optimal implementation of fgets on top of */ -/* fread(). If we end up using this a lot more care should be */ -/* taken. */ -/************************************************************************/ - -char *pj_ctx_fgets(projCtx ctx, char *line, int size, PAFile file) -{ - long start = pj_ctx_ftell(ctx, file); - size_t bytes_read; - int i; - int max_size; - - line[size-1] = '\0'; - bytes_read = pj_ctx_fread(ctx, line, 1, size-1, file); - if(bytes_read == 0) - return NULL; - if(bytes_read < (size_t)size) - { - line[bytes_read] = '\0'; - } - - max_size = (int)MIN(bytes_read, (size_t)(size > 2 ? size - 2 : 0)); - for( i = 0; i < max_size; i++) - { - if (line[i] == '\n') - { - line[i+1] = '\0'; - pj_ctx_fseek(ctx, file, start + i + 1, SEEK_SET); - break; - } - } - return line; -} diff --git a/src/pj_fileapi.cpp b/src/pj_fileapi.cpp new file mode 100644 index 00000000..eba96afd --- /dev/null +++ b/src/pj_fileapi.cpp @@ -0,0 +1,213 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of the pj_ctx_* file api, and the default stdio + * based implementation. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2013, Frank Warmerdam + * + * 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 +#include +#include +#include +#include + +#include "projects.h" + +static PAFile stdio_fopen(projCtx ctx, const char *filename, + const char *access); +static size_t stdio_fread(void *buffer, size_t size, size_t nmemb, + PAFile file); +static int stdio_fseek(PAFile file, long offset, int whence); +static long stdio_ftell(PAFile file); +static void stdio_fclose(PAFile file); + +static projFileAPI default_fileapi = { + stdio_fopen, + stdio_fread, + stdio_fseek, + stdio_ftell, + stdio_fclose +}; + +typedef struct { + projCtx ctx; + FILE *fp; +} stdio_pafile; + +/************************************************************************/ +/* pj_get_default_fileapi() */ +/************************************************************************/ + +projFileAPI *pj_get_default_fileapi(void) +{ + return &default_fileapi; +} + +/************************************************************************/ +/* stdio_fopen() */ +/************************************************************************/ + +static PAFile stdio_fopen(projCtx ctx, const char *filename, + const char *access) +{ + stdio_pafile *pafile; + FILE *fp; + + fp = fopen(filename, access); + if (fp == NULL) + { + return NULL; + } + + pafile = (stdio_pafile *) malloc(sizeof(stdio_pafile)); + if (!pafile) + { + pj_ctx_set_errno(ctx, ENOMEM); + fclose(fp); + return NULL; + } + + pafile->fp = fp; + pafile->ctx = ctx; + return (PAFile) pafile; +} + +/************************************************************************/ +/* stdio_fread() */ +/************************************************************************/ + +static size_t stdio_fread(void *buffer, size_t size, size_t nmemb, + PAFile file) +{ + stdio_pafile *pafile = (stdio_pafile *) file; + return fread(buffer, size, nmemb, pafile->fp); +} + +/************************************************************************/ +/* stdio_fseek() */ +/************************************************************************/ +static int stdio_fseek(PAFile file, long offset, int whence) +{ + stdio_pafile *pafile = (stdio_pafile *) file; + return fseek(pafile->fp, offset, whence); +} + +/************************************************************************/ +/* stdio_ftell() */ +/************************************************************************/ +static long stdio_ftell(PAFile file) +{ + stdio_pafile *pafile = (stdio_pafile *) file; + return ftell(pafile->fp); +} + +/************************************************************************/ +/* stdio_fclose() */ +/************************************************************************/ +static void stdio_fclose(PAFile file) +{ + stdio_pafile *pafile = (stdio_pafile *) file; + fclose(pafile->fp); + free(pafile); +} + +/************************************************************************/ +/* pj_ctx_fopen() */ +/* */ +/* Open a file using the provided file io hooks. */ +/************************************************************************/ + +PAFile pj_ctx_fopen(projCtx ctx, const char *filename, const char *access) +{ + return ctx->fileapi->FOpen(ctx, filename, access); +} + +/************************************************************************/ +/* pj_ctx_fread() */ +/************************************************************************/ +size_t pj_ctx_fread(projCtx ctx, void *buffer, size_t size, size_t nmemb, PAFile file) +{ + return ctx->fileapi->FRead(buffer, size, nmemb, file); +} + +/************************************************************************/ +/* pj_ctx_fseek() */ +/************************************************************************/ +int pj_ctx_fseek(projCtx ctx, PAFile file, long offset, int whence) +{ + return ctx->fileapi->FSeek(file, offset, whence); +} + +/************************************************************************/ +/* pj_ctx_ftell() */ +/************************************************************************/ +long pj_ctx_ftell(projCtx ctx, PAFile file) +{ + return ctx->fileapi->FTell(file); +} + +/************************************************************************/ +/* pj_ctx_fclose() */ +/************************************************************************/ +void pj_ctx_fclose(projCtx ctx, PAFile file) +{ + ctx->fileapi->FClose(file); +} + +/************************************************************************/ +/* pj_ctx_fgets() */ +/* */ +/* A not very optimal implementation of fgets on top of */ +/* fread(). If we end up using this a lot more care should be */ +/* taken. */ +/************************************************************************/ + +char *pj_ctx_fgets(projCtx ctx, char *line, int size, PAFile file) +{ + long start = pj_ctx_ftell(ctx, file); + size_t bytes_read; + int i; + int max_size; + + line[size-1] = '\0'; + bytes_read = pj_ctx_fread(ctx, line, 1, size-1, file); + if(bytes_read == 0) + return NULL; + if(bytes_read < (size_t)size) + { + line[bytes_read] = '\0'; + } + + max_size = (int)MIN(bytes_read, (size_t)(size > 2 ? size - 2 : 0)); + for( i = 0; i < max_size; i++) + { + if (line[i] == '\n') + { + line[i+1] = '\0'; + pj_ctx_fseek(ctx, file, start + i + 1, SEEK_SET); + break; + } + } + return line; +} diff --git a/src/pj_fwd.c b/src/pj_fwd.c deleted file mode 100644 index 38443f07..00000000 --- a/src/pj_fwd.c +++ /dev/null @@ -1,261 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Forward operation invocation - * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 - * Based on material from Gerald Evenden (original pj_fwd) - * and Piyush Agram (original pj_fwd3d) - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * Copyright (c) 2018, Thomas Knudsen / SDFE - * - * 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 -#include - -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -#define INPUT_UNITS P->left -#define OUTPUT_UNITS P->right - - -static PJ_COORD fwd_prepare (PJ *P, PJ_COORD coo) { - if (HUGE_VAL==coo.v[0] || HUGE_VAL==coo.v[1] || HUGE_VAL==coo.v[2]) - return proj_coord_error (); - - /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ - if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; - if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; - - /* Check validity of angular input coordinates */ - if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) { - double t; - - /* check for latitude or longitude over-range */ - t = (coo.lp.phi < 0 ? -coo.lp.phi : coo.lp.phi) - M_HALFPI; - if (t > PJ_EPS_LAT || coo.lp.lam > 10 || coo.lp.lam < -10) { - proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - return proj_coord_error (); - } - - /* Clamp latitude to -90..90 degree range */ - if (coo.lp.phi > M_HALFPI) - coo.lp.phi = M_HALFPI; - if (coo.lp.phi < -M_HALFPI) - coo.lp.phi = -M_HALFPI; - - /* If input latitude is geocentrical, convert to geographical */ - if (P->geoc) - coo = pj_geocentric_latitude (P, PJ_INV, coo); - - /* Ensure longitude is in the -pi:pi range */ - if (0==P->over) - coo.lp.lam = adjlon(coo.lp.lam); - - if (P->hgridshift) - coo = proj_trans (P->hgridshift, PJ_INV, coo); - else if (P->helmert || (P->cart_wgs84 != 0 && P->cart != 0)) { - coo = proj_trans (P->cart_wgs84, PJ_FWD, coo); /* Go cartesian in WGS84 frame */ - if( P->helmert ) - coo = proj_trans (P->helmert, PJ_INV, coo); /* Step into local frame */ - coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ - } - if (coo.lp.lam==HUGE_VAL) - return coo; - if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_FWD, coo); /* Go orthometric from geometric */ - - /* Distance from central meridian, taking system zero meridian into account */ - coo.lp.lam = (coo.lp.lam - P->from_greenwich) - P->lam0; - - /* Ensure longitude is in the -pi:pi range */ - if (0==P->over) - coo.lp.lam = adjlon(coo.lp.lam); - - return coo; - } - - - /* We do not support gridshifts on cartesian input */ - if (INPUT_UNITS==PJ_IO_UNITS_CARTESIAN && P->helmert) - return proj_trans (P->helmert, PJ_INV, coo); - return coo; -} - - -static PJ_COORD fwd_finalize (PJ *P, PJ_COORD coo) { - - switch (OUTPUT_UNITS) { - - /* Handle false eastings/northings and non-metric linear units */ - case PJ_IO_UNITS_CARTESIAN: - - if (P->is_geocent) { - coo = proj_trans (P->cart, PJ_FWD, coo); - } - coo.xyz.x *= P->fr_meter; - coo.xyz.y *= P->fr_meter; - coo.xyz.z *= P->fr_meter; - - break; - - /* Classic proj.4 functions return plane coordinates in units of the semimajor axis */ - case PJ_IO_UNITS_CLASSIC: - coo.xy.x *= P->a; - coo.xy.y *= P->a; - - /* Falls through */ /* (<-- GCC warning silencer) */ - /* to continue processing in common with PJ_IO_UNITS_PROJECTED */ - case PJ_IO_UNITS_PROJECTED: - coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0); - coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0); - coo.xyz.z = P->vfr_meter * (coo.xyz.z + P->z0); - break; - - case PJ_IO_UNITS_WHATEVER: - break; - - case PJ_IO_UNITS_ANGULAR: - coo.lpz.z = P->vfr_meter * (coo.lpz.z + P->z0); - - if( P->is_long_wrap_set ) { - if( coo.lpz.lam != HUGE_VAL ) { - coo.lpz.lam = P->long_wrap_center + - adjlon(coo.lpz.lam - P->long_wrap_center); - } - } - - break; - } - - if (P->axisswap) - coo = proj_trans (P->axisswap, PJ_FWD, coo); - - return coo; -} - - -static PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { - if (proj_errno(P)) - return proj_coord_error(); - - proj_errno_restore(P, last_errno); - return coord; -} - - -XY pj_fwd(LP lp, PJ *P) { - int last_errno; - PJ_COORD coo = {{0,0,0,0}}; - coo.lp = lp; - - last_errno = proj_errno_reset(P); - - if (!P->skip_fwd_prepare) - coo = fwd_prepare (P, coo); - if (HUGE_VAL==coo.v[0] || HUGE_VAL==coo.v[1]) - return proj_coord_error ().xy; - - /* Do the transformation, using the lowest dimensional transformer available */ - if (P->fwd) - coo.xy = P->fwd(coo.lp, P); - else if (P->fwd3d) - coo.xyz = P->fwd3d (coo.lpz, P); - else if (P->fwd4d) - coo = P->fwd4d (coo, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error ().xy; - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().xy; - - if (!P->skip_fwd_finalize) - coo = fwd_finalize (P, coo); - - return error_or_coord(P, coo, last_errno).xy; -} - - - -XYZ pj_fwd3d(LPZ lpz, PJ *P) { - int last_errno; - PJ_COORD coo = {{0,0,0,0}}; - coo.lpz = lpz; - - last_errno = proj_errno_reset(P); - - if (!P->skip_fwd_prepare) - coo = fwd_prepare (P, coo); - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().xyz; - - /* Do the transformation, using the lowest dimensional transformer feasible */ - if (P->fwd3d) - coo.xyz = P->fwd3d(coo.lpz, P); - else if (P->fwd4d) - coo = P->fwd4d (coo, P); - else if (P->fwd) - coo.xy = P->fwd (coo.lp, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error ().xyz; - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().xyz; - - if (!P->skip_fwd_finalize) - coo = fwd_finalize (P, coo); - - return error_or_coord(P, coo, last_errno).xyz; -} - - - -PJ_COORD pj_fwd4d (PJ_COORD coo, PJ *P) { - int last_errno = proj_errno_reset(P); - - if (!P->skip_fwd_prepare) - coo = fwd_prepare (P, coo); - if (HUGE_VAL==coo.v[0]) - return proj_coord_error (); - - /* Call the highest dimensional converter available */ - if (P->fwd4d) - coo = P->fwd4d (coo, P); - else if (P->fwd3d) - coo.xyz = P->fwd3d (coo.lpz, P); - else if (P->fwd) - coo.xy = P->fwd (coo.lp, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error (); - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error (); - - if (!P->skip_fwd_finalize) - coo = fwd_finalize (P, coo); - - return error_or_coord(P, coo, last_errno); -} diff --git a/src/pj_fwd.cpp b/src/pj_fwd.cpp new file mode 100644 index 00000000..38443f07 --- /dev/null +++ b/src/pj_fwd.cpp @@ -0,0 +1,261 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Forward operation invocation + * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 + * Based on material from Gerald Evenden (original pj_fwd) + * and Piyush Agram (original pj_fwd3d) + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2018, Thomas Knudsen / SDFE + * + * 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 +#include + +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +#define INPUT_UNITS P->left +#define OUTPUT_UNITS P->right + + +static PJ_COORD fwd_prepare (PJ *P, PJ_COORD coo) { + if (HUGE_VAL==coo.v[0] || HUGE_VAL==coo.v[1] || HUGE_VAL==coo.v[2]) + return proj_coord_error (); + + /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ + if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; + if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; + + /* Check validity of angular input coordinates */ + if (INPUT_UNITS==PJ_IO_UNITS_ANGULAR) { + double t; + + /* check for latitude or longitude over-range */ + t = (coo.lp.phi < 0 ? -coo.lp.phi : coo.lp.phi) - M_HALFPI; + if (t > PJ_EPS_LAT || coo.lp.lam > 10 || coo.lp.lam < -10) { + proj_errno_set (P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + return proj_coord_error (); + } + + /* Clamp latitude to -90..90 degree range */ + if (coo.lp.phi > M_HALFPI) + coo.lp.phi = M_HALFPI; + if (coo.lp.phi < -M_HALFPI) + coo.lp.phi = -M_HALFPI; + + /* If input latitude is geocentrical, convert to geographical */ + if (P->geoc) + coo = pj_geocentric_latitude (P, PJ_INV, coo); + + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); + + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_INV, coo); + else if (P->helmert || (P->cart_wgs84 != 0 && P->cart != 0)) { + coo = proj_trans (P->cart_wgs84, PJ_FWD, coo); /* Go cartesian in WGS84 frame */ + if( P->helmert ) + coo = proj_trans (P->helmert, PJ_INV, coo); /* Step into local frame */ + coo = proj_trans (P->cart, PJ_INV, coo); /* Go back to angular using local ellps */ + } + if (coo.lp.lam==HUGE_VAL) + return coo; + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_FWD, coo); /* Go orthometric from geometric */ + + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = (coo.lp.lam - P->from_greenwich) - P->lam0; + + /* Ensure longitude is in the -pi:pi range */ + if (0==P->over) + coo.lp.lam = adjlon(coo.lp.lam); + + return coo; + } + + + /* We do not support gridshifts on cartesian input */ + if (INPUT_UNITS==PJ_IO_UNITS_CARTESIAN && P->helmert) + return proj_trans (P->helmert, PJ_INV, coo); + return coo; +} + + +static PJ_COORD fwd_finalize (PJ *P, PJ_COORD coo) { + + switch (OUTPUT_UNITS) { + + /* Handle false eastings/northings and non-metric linear units */ + case PJ_IO_UNITS_CARTESIAN: + + if (P->is_geocent) { + coo = proj_trans (P->cart, PJ_FWD, coo); + } + coo.xyz.x *= P->fr_meter; + coo.xyz.y *= P->fr_meter; + coo.xyz.z *= P->fr_meter; + + break; + + /* Classic proj.4 functions return plane coordinates in units of the semimajor axis */ + case PJ_IO_UNITS_CLASSIC: + coo.xy.x *= P->a; + coo.xy.y *= P->a; + + /* Falls through */ /* (<-- GCC warning silencer) */ + /* to continue processing in common with PJ_IO_UNITS_PROJECTED */ + case PJ_IO_UNITS_PROJECTED: + coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0); + coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0); + coo.xyz.z = P->vfr_meter * (coo.xyz.z + P->z0); + break; + + case PJ_IO_UNITS_WHATEVER: + break; + + case PJ_IO_UNITS_ANGULAR: + coo.lpz.z = P->vfr_meter * (coo.lpz.z + P->z0); + + if( P->is_long_wrap_set ) { + if( coo.lpz.lam != HUGE_VAL ) { + coo.lpz.lam = P->long_wrap_center + + adjlon(coo.lpz.lam - P->long_wrap_center); + } + } + + break; + } + + if (P->axisswap) + coo = proj_trans (P->axisswap, PJ_FWD, coo); + + return coo; +} + + +static PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { + if (proj_errno(P)) + return proj_coord_error(); + + proj_errno_restore(P, last_errno); + return coord; +} + + +XY pj_fwd(LP lp, PJ *P) { + int last_errno; + PJ_COORD coo = {{0,0,0,0}}; + coo.lp = lp; + + last_errno = proj_errno_reset(P); + + if (!P->skip_fwd_prepare) + coo = fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0] || HUGE_VAL==coo.v[1]) + return proj_coord_error ().xy; + + /* Do the transformation, using the lowest dimensional transformer available */ + if (P->fwd) + coo.xy = P->fwd(coo.lp, P); + else if (P->fwd3d) + coo.xyz = P->fwd3d (coo.lpz, P); + else if (P->fwd4d) + coo = P->fwd4d (coo, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().xy; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xy; + + if (!P->skip_fwd_finalize) + coo = fwd_finalize (P, coo); + + return error_or_coord(P, coo, last_errno).xy; +} + + + +XYZ pj_fwd3d(LPZ lpz, PJ *P) { + int last_errno; + PJ_COORD coo = {{0,0,0,0}}; + coo.lpz = lpz; + + last_errno = proj_errno_reset(P); + + if (!P->skip_fwd_prepare) + coo = fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xyz; + + /* Do the transformation, using the lowest dimensional transformer feasible */ + if (P->fwd3d) + coo.xyz = P->fwd3d(coo.lpz, P); + else if (P->fwd4d) + coo = P->fwd4d (coo, P); + else if (P->fwd) + coo.xy = P->fwd (coo.lp, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().xyz; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().xyz; + + if (!P->skip_fwd_finalize) + coo = fwd_finalize (P, coo); + + return error_or_coord(P, coo, last_errno).xyz; +} + + + +PJ_COORD pj_fwd4d (PJ_COORD coo, PJ *P) { + int last_errno = proj_errno_reset(P); + + if (!P->skip_fwd_prepare) + coo = fwd_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); + + /* Call the highest dimensional converter available */ + if (P->fwd4d) + coo = P->fwd4d (coo, P); + else if (P->fwd3d) + coo.xyz = P->fwd3d (coo.lpz, P); + else if (P->fwd) + coo.xy = P->fwd (coo.lp, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error (); + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); + + if (!P->skip_fwd_finalize) + coo = fwd_finalize (P, coo); + + return error_or_coord(P, coo, last_errno); +} diff --git a/src/pj_gauss.c b/src/pj_gauss.c deleted file mode 100644 index 4520bb39..00000000 --- a/src/pj_gauss.c +++ /dev/null @@ -1,101 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2003 Gerald I. Evenden -*/ -/* -** 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. -*/ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -#define MAX_ITER 20 - -struct GAUSS { - double C; - double K; - double e; - double ratexp; -}; -#define DEL_TOL 1e-14 - -static double srat(double esinp, double ratexp) { - return(pow((1.-esinp)/(1.+esinp), ratexp)); -} - -void *pj_gauss_ini(double e, double phi0, double *chi, double *rc) { - double sphi, cphi, es; - struct GAUSS *en; - - if ((en = (struct GAUSS *)malloc(sizeof(struct GAUSS))) == NULL) - return (NULL); - es = e * e; - en->e = e; - sphi = sin(phi0); - cphi = cos(phi0); cphi *= cphi; - *rc = sqrt(1. - es) / (1. - es * sphi * sphi); - en->C = sqrt(1. + es * cphi * cphi / (1. - es)); - if (en->C == 0.0) { - free(en); - return NULL; - } - *chi = asin(sphi / en->C); - en->ratexp = 0.5 * en->C * e; - en->K = tan(.5 * *chi + M_FORTPI) / ( - pow(tan(.5 * phi0 + M_FORTPI), en->C) * - srat(en->e * sphi, en->ratexp) ); - return ((void *)en); -} - -LP pj_gauss(projCtx ctx, LP elp, const void *data) { - const struct GAUSS *en = (const struct GAUSS *)data; - LP slp; - (void) ctx; - - slp.phi = 2. * atan( en->K * - pow(tan(.5 * elp.phi + M_FORTPI), en->C) * - srat(en->e * sin(elp.phi), en->ratexp) ) - M_HALFPI; - slp.lam = en->C * (elp.lam); - return(slp); -} - -LP pj_inv_gauss(projCtx ctx, LP slp, const void *data) { - const struct GAUSS *en = (const struct GAUSS *)data; - LP elp; - double num; - int i; - - elp.lam = slp.lam / en->C; - num = pow(tan(.5 * slp.phi + M_FORTPI)/en->K, 1./en->C); - for (i = MAX_ITER; i; --i) { - elp.phi = 2. * atan(num * srat(en->e * sin(slp.phi), -.5 * en->e)) - - M_HALFPI; - if (fabs(elp.phi - slp.phi) < DEL_TOL) break; - slp.phi = elp.phi; - } - /* convergence failed */ - if (!i) - pj_ctx_set_errno(ctx, PJD_ERR_NON_CONV_INV_MERI_DIST); - return (elp); -} diff --git a/src/pj_gauss.cpp b/src/pj_gauss.cpp new file mode 100644 index 00000000..4520bb39 --- /dev/null +++ b/src/pj_gauss.cpp @@ -0,0 +1,101 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2003 Gerald I. Evenden +*/ +/* +** 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. +*/ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +#define MAX_ITER 20 + +struct GAUSS { + double C; + double K; + double e; + double ratexp; +}; +#define DEL_TOL 1e-14 + +static double srat(double esinp, double ratexp) { + return(pow((1.-esinp)/(1.+esinp), ratexp)); +} + +void *pj_gauss_ini(double e, double phi0, double *chi, double *rc) { + double sphi, cphi, es; + struct GAUSS *en; + + if ((en = (struct GAUSS *)malloc(sizeof(struct GAUSS))) == NULL) + return (NULL); + es = e * e; + en->e = e; + sphi = sin(phi0); + cphi = cos(phi0); cphi *= cphi; + *rc = sqrt(1. - es) / (1. - es * sphi * sphi); + en->C = sqrt(1. + es * cphi * cphi / (1. - es)); + if (en->C == 0.0) { + free(en); + return NULL; + } + *chi = asin(sphi / en->C); + en->ratexp = 0.5 * en->C * e; + en->K = tan(.5 * *chi + M_FORTPI) / ( + pow(tan(.5 * phi0 + M_FORTPI), en->C) * + srat(en->e * sphi, en->ratexp) ); + return ((void *)en); +} + +LP pj_gauss(projCtx ctx, LP elp, const void *data) { + const struct GAUSS *en = (const struct GAUSS *)data; + LP slp; + (void) ctx; + + slp.phi = 2. * atan( en->K * + pow(tan(.5 * elp.phi + M_FORTPI), en->C) * + srat(en->e * sin(elp.phi), en->ratexp) ) - M_HALFPI; + slp.lam = en->C * (elp.lam); + return(slp); +} + +LP pj_inv_gauss(projCtx ctx, LP slp, const void *data) { + const struct GAUSS *en = (const struct GAUSS *)data; + LP elp; + double num; + int i; + + elp.lam = slp.lam / en->C; + num = pow(tan(.5 * slp.phi + M_FORTPI)/en->K, 1./en->C); + for (i = MAX_ITER; i; --i) { + elp.phi = 2. * atan(num * srat(en->e * sin(slp.phi), -.5 * en->e)) + - M_HALFPI; + if (fabs(elp.phi - slp.phi) < DEL_TOL) break; + slp.phi = elp.phi; + } + /* convergence failed */ + if (!i) + pj_ctx_set_errno(ctx, PJD_ERR_NON_CONV_INV_MERI_DIST); + return (elp); +} diff --git a/src/pj_gc_reader.c b/src/pj_gc_reader.c deleted file mode 100644 index 493fc075..00000000 --- a/src/pj_gc_reader.c +++ /dev/null @@ -1,247 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code to read a grid catalog from a .cvs file. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2012, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "projects.h" - -static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry); - -/************************************************************************/ -/* pj_gc_readcatalog() */ -/* */ -/* Read a grid catalog from a .csv file. */ -/************************************************************************/ - -PJ_GridCatalog *pj_gc_readcatalog( projCtx ctx, const char *catalog_name ) -{ - PAFile fid; - PJ_GridCatalog *catalog; - int entry_max; - char line[302]; - - fid = pj_open_lib( ctx, catalog_name, "r" ); - if (fid == NULL) - return NULL; - - /* discard title line */ - pj_ctx_fgets(ctx, line, sizeof(line)-1, fid); - - catalog = (PJ_GridCatalog *) calloc(1,sizeof(PJ_GridCatalog)); - if( !catalog ) - { - pj_ctx_set_errno(ctx, ENOMEM); - pj_ctx_fclose(ctx, fid); - return NULL; - } - - catalog->catalog_name = pj_strdup(catalog_name); - if (!catalog->catalog_name) { - pj_ctx_set_errno(ctx, ENOMEM); - free(catalog); - pj_ctx_fclose(ctx, fid); - return NULL; - } - - entry_max = 10; - catalog->entries = (PJ_GridCatalogEntry *) - malloc(entry_max * sizeof(PJ_GridCatalogEntry)); - if (!catalog->entries) { - pj_ctx_set_errno(ctx, ENOMEM); - free(catalog->catalog_name); - free(catalog); - pj_ctx_fclose(ctx, fid); - return NULL; - } - - while( gc_readentry( ctx, fid, - catalog->entries+catalog->entry_count) == 0) - { - catalog->entry_count++; - - if( catalog->entry_count == entry_max ) - { - PJ_GridCatalogEntry* new_entries; - entry_max = entry_max * 2; - new_entries = (PJ_GridCatalogEntry *) - realloc(catalog->entries, - entry_max * sizeof(PJ_GridCatalogEntry)); - if (new_entries == NULL ) - { - int i; - for( i = 0; i < catalog->entry_count; i++ ) - free( catalog->entries[i].definition ); - free( catalog->entries ); - free( catalog->catalog_name ); - free( catalog ); - pj_ctx_fclose(ctx, fid); - return NULL; - } - catalog->entries = new_entries; - } - } - - pj_ctx_fclose(ctx, fid); - - return catalog; -} - -/************************************************************************/ -/* gc_read_csv_line() */ -/* */ -/* Simple csv line splitter with fixed maximum line size and */ -/* token count. */ -/************************************************************************/ - -static int gc_read_csv_line( projCtx ctx, PAFile fid, - char **tokens, int max_tokens ) -{ - char line[302]; - - while( pj_ctx_fgets(ctx, line, sizeof(line)-1, fid) != NULL ) - { - char *next = line; - int token_count = 0; - - while( isspace(*next) ) - next++; - - /* skip blank and comment lines */ - if( next[0] == '#' || next[0] == '\0' ) - continue; - - while( token_count < max_tokens && *next != '\0' ) - { - const char *start = next; - char* token; - - while( *next != '\0' && *next != ',' ) - next++; - - if( *next == ',' ) - { - *next = '\0'; - next++; - } - - token = pj_strdup(start); - if (!token) { - while (token_count > 0) - free(tokens[--token_count]); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - tokens[token_count++] = token; - } - - return token_count; - } - - return 0; -} - -/************************************************************************/ -/* pj_gc_parsedate() */ -/* */ -/* Parse a date into a floating point year value. Acceptable */ -/* values are "yyyy.fraction" and "yyyy-mm-dd". Anything else */ -/* returns 0.0. */ -/************************************************************************/ - -double pj_gc_parsedate( projCtx ctx, const char *date_string ) -{ - (void) ctx; - - if( strlen(date_string) == 10 - && date_string[4] == '-' && date_string[7] == '-' ) - { - int year = atoi(date_string); - int month = atoi(date_string+5); - int day = atoi(date_string+8); - - /* simplified calculation so we don't need to know all about months */ - return year + ((month-1) * 31 + (day-1)) / 372.0; - } - else - { - return pj_atof(date_string); - } -} - - -/************************************************************************/ -/* gc_readentry() */ -/* */ -/* Read one catalog entry from the file */ -/* */ -/* Format: */ -/* gridname,ll_long,ll_lat,ur_long,ur_lat,priority,date */ -/************************************************************************/ - -static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry) -{ -#define MAX_TOKENS 30 - char *tokens[MAX_TOKENS]; - int token_count, i; - int error = 0; - - memset( entry, 0, sizeof(PJ_GridCatalogEntry) ); - - token_count = gc_read_csv_line( ctx, fid, tokens, MAX_TOKENS ); - if( token_count < 5 ) - { - error = 1; /* TODO: need real error codes */ - if( token_count != 0 ) - pj_log( ctx, PJ_LOG_ERROR, "Short line in grid catalog." ); - } - else - { - entry->definition = tokens[0]; - tokens[0] = NULL; /* We take ownership of tokens[0] */ - entry->region.ll_long = dmstor_ctx( ctx, tokens[1], NULL ); - entry->region.ll_lat = dmstor_ctx( ctx, tokens[2], NULL ); - entry->region.ur_long = dmstor_ctx( ctx, tokens[3], NULL ); - entry->region.ur_lat = dmstor_ctx( ctx, tokens[4], NULL ); - if( token_count > 5 ) - entry->priority = atoi( tokens[5] ); /* defaults to zero */ - if( token_count > 6 ) - entry->date = pj_gc_parsedate( ctx, tokens[6] ); - } - - for( i = 0; i < token_count; i++ ) - free( tokens[i] ); - - return error; -} - - - diff --git a/src/pj_gc_reader.cpp b/src/pj_gc_reader.cpp new file mode 100644 index 00000000..493fc075 --- /dev/null +++ b/src/pj_gc_reader.cpp @@ -0,0 +1,247 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Code to read a grid catalog from a .cvs file. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2012, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "projects.h" + +static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry); + +/************************************************************************/ +/* pj_gc_readcatalog() */ +/* */ +/* Read a grid catalog from a .csv file. */ +/************************************************************************/ + +PJ_GridCatalog *pj_gc_readcatalog( projCtx ctx, const char *catalog_name ) +{ + PAFile fid; + PJ_GridCatalog *catalog; + int entry_max; + char line[302]; + + fid = pj_open_lib( ctx, catalog_name, "r" ); + if (fid == NULL) + return NULL; + + /* discard title line */ + pj_ctx_fgets(ctx, line, sizeof(line)-1, fid); + + catalog = (PJ_GridCatalog *) calloc(1,sizeof(PJ_GridCatalog)); + if( !catalog ) + { + pj_ctx_set_errno(ctx, ENOMEM); + pj_ctx_fclose(ctx, fid); + return NULL; + } + + catalog->catalog_name = pj_strdup(catalog_name); + if (!catalog->catalog_name) { + pj_ctx_set_errno(ctx, ENOMEM); + free(catalog); + pj_ctx_fclose(ctx, fid); + return NULL; + } + + entry_max = 10; + catalog->entries = (PJ_GridCatalogEntry *) + malloc(entry_max * sizeof(PJ_GridCatalogEntry)); + if (!catalog->entries) { + pj_ctx_set_errno(ctx, ENOMEM); + free(catalog->catalog_name); + free(catalog); + pj_ctx_fclose(ctx, fid); + return NULL; + } + + while( gc_readentry( ctx, fid, + catalog->entries+catalog->entry_count) == 0) + { + catalog->entry_count++; + + if( catalog->entry_count == entry_max ) + { + PJ_GridCatalogEntry* new_entries; + entry_max = entry_max * 2; + new_entries = (PJ_GridCatalogEntry *) + realloc(catalog->entries, + entry_max * sizeof(PJ_GridCatalogEntry)); + if (new_entries == NULL ) + { + int i; + for( i = 0; i < catalog->entry_count; i++ ) + free( catalog->entries[i].definition ); + free( catalog->entries ); + free( catalog->catalog_name ); + free( catalog ); + pj_ctx_fclose(ctx, fid); + return NULL; + } + catalog->entries = new_entries; + } + } + + pj_ctx_fclose(ctx, fid); + + return catalog; +} + +/************************************************************************/ +/* gc_read_csv_line() */ +/* */ +/* Simple csv line splitter with fixed maximum line size and */ +/* token count. */ +/************************************************************************/ + +static int gc_read_csv_line( projCtx ctx, PAFile fid, + char **tokens, int max_tokens ) +{ + char line[302]; + + while( pj_ctx_fgets(ctx, line, sizeof(line)-1, fid) != NULL ) + { + char *next = line; + int token_count = 0; + + while( isspace(*next) ) + next++; + + /* skip blank and comment lines */ + if( next[0] == '#' || next[0] == '\0' ) + continue; + + while( token_count < max_tokens && *next != '\0' ) + { + const char *start = next; + char* token; + + while( *next != '\0' && *next != ',' ) + next++; + + if( *next == ',' ) + { + *next = '\0'; + next++; + } + + token = pj_strdup(start); + if (!token) { + while (token_count > 0) + free(tokens[--token_count]); + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + tokens[token_count++] = token; + } + + return token_count; + } + + return 0; +} + +/************************************************************************/ +/* pj_gc_parsedate() */ +/* */ +/* Parse a date into a floating point year value. Acceptable */ +/* values are "yyyy.fraction" and "yyyy-mm-dd". Anything else */ +/* returns 0.0. */ +/************************************************************************/ + +double pj_gc_parsedate( projCtx ctx, const char *date_string ) +{ + (void) ctx; + + if( strlen(date_string) == 10 + && date_string[4] == '-' && date_string[7] == '-' ) + { + int year = atoi(date_string); + int month = atoi(date_string+5); + int day = atoi(date_string+8); + + /* simplified calculation so we don't need to know all about months */ + return year + ((month-1) * 31 + (day-1)) / 372.0; + } + else + { + return pj_atof(date_string); + } +} + + +/************************************************************************/ +/* gc_readentry() */ +/* */ +/* Read one catalog entry from the file */ +/* */ +/* Format: */ +/* gridname,ll_long,ll_lat,ur_long,ur_lat,priority,date */ +/************************************************************************/ + +static int gc_readentry(projCtx ctx, PAFile fid, PJ_GridCatalogEntry *entry) +{ +#define MAX_TOKENS 30 + char *tokens[MAX_TOKENS]; + int token_count, i; + int error = 0; + + memset( entry, 0, sizeof(PJ_GridCatalogEntry) ); + + token_count = gc_read_csv_line( ctx, fid, tokens, MAX_TOKENS ); + if( token_count < 5 ) + { + error = 1; /* TODO: need real error codes */ + if( token_count != 0 ) + pj_log( ctx, PJ_LOG_ERROR, "Short line in grid catalog." ); + } + else + { + entry->definition = tokens[0]; + tokens[0] = NULL; /* We take ownership of tokens[0] */ + entry->region.ll_long = dmstor_ctx( ctx, tokens[1], NULL ); + entry->region.ll_lat = dmstor_ctx( ctx, tokens[2], NULL ); + entry->region.ur_long = dmstor_ctx( ctx, tokens[3], NULL ); + entry->region.ur_lat = dmstor_ctx( ctx, tokens[4], NULL ); + if( token_count > 5 ) + entry->priority = atoi( tokens[5] ); /* defaults to zero */ + if( token_count > 6 ) + entry->date = pj_gc_parsedate( ctx, tokens[6] ); + } + + for( i = 0; i < token_count; i++ ) + free( tokens[i] ); + + return error; +} + + + diff --git a/src/pj_geocent.c b/src/pj_geocent.c deleted file mode 100644 index 0e9d725e..00000000 --- a/src/pj_geocent.c +++ /dev/null @@ -1,62 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Stub projection for geocentric. The transformation isn't - * really done here since this code is 2D. The real transformation - * is handled by pj_transform.c. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2002, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ -#include "projects.h" - -PROJ_HEAD(geocent, "Geocentric") "\n\t"; - -static XY forward(LP lp, PJ *P) { - XY xy = {0.0,0.0}; - (void) P; - xy.x = lp.lam; - xy.y = lp.phi; - return xy; -} - -static LP inverse(XY xy, PJ *P) { - LP lp = {0.0,0.0}; - (void) P; - lp.phi = xy.y; - lp.lam = xy.x; - return lp; -} - -PJ *CONVERSION (geocent, 0) { - P->is_geocent = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; - P->fwd = forward; - P->left = PJ_IO_UNITS_ANGULAR; - P->right = PJ_IO_UNITS_CARTESIAN; - - return P; -} - diff --git a/src/pj_geocent.cpp b/src/pj_geocent.cpp new file mode 100644 index 00000000..0e9d725e --- /dev/null +++ b/src/pj_geocent.cpp @@ -0,0 +1,62 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Stub projection for geocentric. The transformation isn't + * really done here since this code is 2D. The real transformation + * is handled by pj_transform.c. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2002, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ +#include "projects.h" + +PROJ_HEAD(geocent, "Geocentric") "\n\t"; + +static XY forward(LP lp, PJ *P) { + XY xy = {0.0,0.0}; + (void) P; + xy.x = lp.lam; + xy.y = lp.phi; + return xy; +} + +static LP inverse(XY xy, PJ *P) { + LP lp = {0.0,0.0}; + (void) P; + lp.phi = xy.y; + lp.lam = xy.x; + return lp; +} + +PJ *CONVERSION (geocent, 0) { + P->is_geocent = 1; + P->x0 = 0.0; + P->y0 = 0.0; + P->inv = inverse; + P->fwd = forward; + P->left = PJ_IO_UNITS_ANGULAR; + P->right = PJ_IO_UNITS_CARTESIAN; + + return P; +} + diff --git a/src/pj_gridcatalog.c b/src/pj_gridcatalog.c deleted file mode 100644 index ea9c4aa1..00000000 --- a/src/pj_gridcatalog.c +++ /dev/null @@ -1,295 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code in support of grid catalogs - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2012, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include - -#include "projects.h" - -static PJ_GridCatalog *grid_catalog_list = NULL; - -/************************************************************************/ -/* pj_gc_unloadall() */ -/* */ -/* Deallocate all the grid catalogs (but not the referenced */ -/* grids). */ -/************************************************************************/ - -void pj_gc_unloadall( projCtx ctx ) -{ - (void) ctx; - - while( grid_catalog_list != NULL ) - { - int i; - PJ_GridCatalog *catalog = grid_catalog_list; - grid_catalog_list = grid_catalog_list->next; - - for( i = 0; i < catalog->entry_count; i++ ) - { - /* we don't own gridinfo - do not free here */ - free( catalog->entries[i].definition ); - } - free( catalog->entries ); - free( catalog->catalog_name ); - free( catalog ); - } -} - -/************************************************************************/ -/* pj_gc_findcatalog() */ -/************************************************************************/ - -PJ_GridCatalog *pj_gc_findcatalog( projCtx ctx, const char *name ) - -{ - PJ_GridCatalog *catalog; - - pj_acquire_lock(); - - for( catalog=grid_catalog_list; catalog != NULL; catalog = catalog->next ) - { - if( strcmp(catalog->catalog_name, name) == 0 ) - { - pj_release_lock(); - return catalog; - } - } - - pj_release_lock(); - - catalog = pj_gc_readcatalog( ctx, name ); - if( catalog == NULL ) - return NULL; - - pj_acquire_lock(); - catalog->next = grid_catalog_list; - grid_catalog_list = catalog; - pj_release_lock(); - - return catalog; -} - -/************************************************************************/ -/* pj_gc_apply_gridshift() */ -/************************************************************************/ - -int pj_gc_apply_gridshift( PJ *defn, int inverse, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - (void) z; - - if( defn->catalog == NULL ) - { - defn->catalog = pj_gc_findcatalog( defn->ctx, defn->catalog_name ); - if( defn->catalog == NULL ) - return defn->ctx->last_errno; - } - - defn->ctx->last_errno = 0; - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - LP input, output_after, output_before; - double mix_ratio; - PJ_GRIDINFO *gi; - - input.phi = y[io]; - input.lam = x[io]; - - /* make sure we have appropriate "after" shift file available */ - if( defn->last_after_grid == NULL - || input.lam < defn->last_after_region.ll_long - || input.lam > defn->last_after_region.ur_long - || input.phi < defn->last_after_region.ll_lat - || input.phi > defn->last_after_region.ll_lat ) { - defn->last_after_grid = - pj_gc_findgrid( defn->ctx, defn->catalog, - 1, input, defn->datum_date, - &(defn->last_after_region), - &(defn->last_after_date)); - if( defn->last_after_grid == NULL ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - } - gi = defn->last_after_grid; - assert( gi->child == NULL ); - - /* load the grid shift info if we don't have it. */ - if( gi->ct->cvs == NULL && !pj_gridinfo_load( defn->ctx, gi ) ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - output_after = nad_cvt( input, inverse, gi->ct ); - if( output_after.lam == HUGE_VAL ) - { - if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) - { - pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - } - continue; - } - - if( defn->datum_date == 0.0 ) - { - y[io] = output_after.phi; - x[io] = output_after.lam; - continue; - } - - /* make sure we have appropriate "before" shift file available */ - if( defn->last_before_grid == NULL - || input.lam < defn->last_before_region.ll_long - || input.lam > defn->last_before_region.ur_long - || input.phi < defn->last_before_region.ll_lat - || input.phi > defn->last_before_region.ll_lat ) { - defn->last_before_grid = - pj_gc_findgrid( defn->ctx, defn->catalog, - 0, input, defn->datum_date, - &(defn->last_before_region), - &(defn->last_before_date)); - if( defn->last_before_grid == NULL ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - } - - gi = defn->last_before_grid; - assert( gi->child == NULL ); - - /* load the grid shift info if we don't have it. */ - if( gi->ct->cvs == NULL && !pj_gridinfo_load( defn->ctx, gi ) ) - { - pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return PJD_ERR_FAILED_TO_LOAD_GRID; - } - - output_before = nad_cvt( input, inverse, gi->ct ); - if( output_before.lam == HUGE_VAL ) - { - if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) - { - pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, - "pj_apply_gridshift(): failed to find a grid shift table for\n" - " location (%.7fdW,%.7fdN)", - x[io] * RAD_TO_DEG, - y[io] * RAD_TO_DEG ); - } - continue; - } - - mix_ratio = (defn->datum_date - defn->last_before_date) - / (defn->last_after_date - defn->last_before_date); - - y[io] = mix_ratio * output_after.phi - + (1.0-mix_ratio) * output_before.phi; - x[io] = mix_ratio * output_after.lam - + (1.0-mix_ratio) * output_before.lam; - } - - return 0; -} - -/************************************************************************/ -/* pj_c_findgrid() */ -/************************************************************************/ - -PJ_GRIDINFO *pj_gc_findgrid( projCtx ctx, PJ_GridCatalog *catalog, int after, - LP location, double date, - PJ_Region *optional_region, - double *grid_date ) -{ - int iEntry; - PJ_GridCatalogEntry *entry = NULL; - - for( iEntry = 0; iEntry < catalog->entry_count; iEntry++ ) - { - entry = catalog->entries + iEntry; - - if( (after && entry->date < date) - || (!after && entry->date > date) ) - continue; - - if( location.lam < entry->region.ll_long - || location.lam > entry->region.ur_long - || location.phi < entry->region.ll_lat - || location.phi > entry->region.ur_lat ) - continue; - - if( entry->available == -1 ) - continue; - - break; - } - - if( entry == NULL ) - { - if( grid_date ) - *grid_date = 0.0; - if( optional_region != NULL ) - memset( optional_region, 0, sizeof(PJ_Region)); - return NULL; - } - - if( grid_date ) - *grid_date = entry->date; - - if( optional_region ) - { - - } - - if( entry->gridinfo == NULL ) - { - PJ_GRIDINFO **gridlist = NULL; - int grid_count = 0; - gridlist = pj_gridlist_from_nadgrids( ctx, entry->definition, - &grid_count); - if( grid_count == 1 ) - entry->gridinfo = gridlist[0]; - } - - return entry->gridinfo; -} - diff --git a/src/pj_gridcatalog.cpp b/src/pj_gridcatalog.cpp new file mode 100644 index 00000000..ea9c4aa1 --- /dev/null +++ b/src/pj_gridcatalog.cpp @@ -0,0 +1,295 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Code in support of grid catalogs + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2012, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include + +#include "projects.h" + +static PJ_GridCatalog *grid_catalog_list = NULL; + +/************************************************************************/ +/* pj_gc_unloadall() */ +/* */ +/* Deallocate all the grid catalogs (but not the referenced */ +/* grids). */ +/************************************************************************/ + +void pj_gc_unloadall( projCtx ctx ) +{ + (void) ctx; + + while( grid_catalog_list != NULL ) + { + int i; + PJ_GridCatalog *catalog = grid_catalog_list; + grid_catalog_list = grid_catalog_list->next; + + for( i = 0; i < catalog->entry_count; i++ ) + { + /* we don't own gridinfo - do not free here */ + free( catalog->entries[i].definition ); + } + free( catalog->entries ); + free( catalog->catalog_name ); + free( catalog ); + } +} + +/************************************************************************/ +/* pj_gc_findcatalog() */ +/************************************************************************/ + +PJ_GridCatalog *pj_gc_findcatalog( projCtx ctx, const char *name ) + +{ + PJ_GridCatalog *catalog; + + pj_acquire_lock(); + + for( catalog=grid_catalog_list; catalog != NULL; catalog = catalog->next ) + { + if( strcmp(catalog->catalog_name, name) == 0 ) + { + pj_release_lock(); + return catalog; + } + } + + pj_release_lock(); + + catalog = pj_gc_readcatalog( ctx, name ); + if( catalog == NULL ) + return NULL; + + pj_acquire_lock(); + catalog->next = grid_catalog_list; + grid_catalog_list = catalog; + pj_release_lock(); + + return catalog; +} + +/************************************************************************/ +/* pj_gc_apply_gridshift() */ +/************************************************************************/ + +int pj_gc_apply_gridshift( PJ *defn, int inverse, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + (void) z; + + if( defn->catalog == NULL ) + { + defn->catalog = pj_gc_findcatalog( defn->ctx, defn->catalog_name ); + if( defn->catalog == NULL ) + return defn->ctx->last_errno; + } + + defn->ctx->last_errno = 0; + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + LP input, output_after, output_before; + double mix_ratio; + PJ_GRIDINFO *gi; + + input.phi = y[io]; + input.lam = x[io]; + + /* make sure we have appropriate "after" shift file available */ + if( defn->last_after_grid == NULL + || input.lam < defn->last_after_region.ll_long + || input.lam > defn->last_after_region.ur_long + || input.phi < defn->last_after_region.ll_lat + || input.phi > defn->last_after_region.ll_lat ) { + defn->last_after_grid = + pj_gc_findgrid( defn->ctx, defn->catalog, + 1, input, defn->datum_date, + &(defn->last_after_region), + &(defn->last_after_date)); + if( defn->last_after_grid == NULL ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + } + gi = defn->last_after_grid; + assert( gi->child == NULL ); + + /* load the grid shift info if we don't have it. */ + if( gi->ct->cvs == NULL && !pj_gridinfo_load( defn->ctx, gi ) ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + + output_after = nad_cvt( input, inverse, gi->ct ); + if( output_after.lam == HUGE_VAL ) + { + if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + { + pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, + "pj_apply_gridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + } + continue; + } + + if( defn->datum_date == 0.0 ) + { + y[io] = output_after.phi; + x[io] = output_after.lam; + continue; + } + + /* make sure we have appropriate "before" shift file available */ + if( defn->last_before_grid == NULL + || input.lam < defn->last_before_region.ll_long + || input.lam > defn->last_before_region.ur_long + || input.phi < defn->last_before_region.ll_lat + || input.phi > defn->last_before_region.ll_lat ) { + defn->last_before_grid = + pj_gc_findgrid( defn->ctx, defn->catalog, + 0, input, defn->datum_date, + &(defn->last_before_region), + &(defn->last_before_date)); + if( defn->last_before_grid == NULL ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + } + + gi = defn->last_before_grid; + assert( gi->child == NULL ); + + /* load the grid shift info if we don't have it. */ + if( gi->ct->cvs == NULL && !pj_gridinfo_load( defn->ctx, gi ) ) + { + pj_ctx_set_errno( defn->ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return PJD_ERR_FAILED_TO_LOAD_GRID; + } + + output_before = nad_cvt( input, inverse, gi->ct ); + if( output_before.lam == HUGE_VAL ) + { + if( defn->ctx->debug_level >= PJ_LOG_DEBUG_MAJOR ) + { + pj_log( defn->ctx, PJ_LOG_DEBUG_MAJOR, + "pj_apply_gridshift(): failed to find a grid shift table for\n" + " location (%.7fdW,%.7fdN)", + x[io] * RAD_TO_DEG, + y[io] * RAD_TO_DEG ); + } + continue; + } + + mix_ratio = (defn->datum_date - defn->last_before_date) + / (defn->last_after_date - defn->last_before_date); + + y[io] = mix_ratio * output_after.phi + + (1.0-mix_ratio) * output_before.phi; + x[io] = mix_ratio * output_after.lam + + (1.0-mix_ratio) * output_before.lam; + } + + return 0; +} + +/************************************************************************/ +/* pj_c_findgrid() */ +/************************************************************************/ + +PJ_GRIDINFO *pj_gc_findgrid( projCtx ctx, PJ_GridCatalog *catalog, int after, + LP location, double date, + PJ_Region *optional_region, + double *grid_date ) +{ + int iEntry; + PJ_GridCatalogEntry *entry = NULL; + + for( iEntry = 0; iEntry < catalog->entry_count; iEntry++ ) + { + entry = catalog->entries + iEntry; + + if( (after && entry->date < date) + || (!after && entry->date > date) ) + continue; + + if( location.lam < entry->region.ll_long + || location.lam > entry->region.ur_long + || location.phi < entry->region.ll_lat + || location.phi > entry->region.ur_lat ) + continue; + + if( entry->available == -1 ) + continue; + + break; + } + + if( entry == NULL ) + { + if( grid_date ) + *grid_date = 0.0; + if( optional_region != NULL ) + memset( optional_region, 0, sizeof(PJ_Region)); + return NULL; + } + + if( grid_date ) + *grid_date = entry->date; + + if( optional_region ) + { + + } + + if( entry->gridinfo == NULL ) + { + PJ_GRIDINFO **gridlist = NULL; + int grid_count = 0; + gridlist = pj_gridlist_from_nadgrids( ctx, entry->definition, + &grid_count); + if( grid_count == 1 ) + entry->gridinfo = gridlist[0]; + } + + return entry->gridinfo; +} + diff --git a/src/pj_gridinfo.c b/src/pj_gridinfo.c deleted file mode 100644 index de0e8d31..00000000 --- a/src/pj_gridinfo.c +++ /dev/null @@ -1,991 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Functions for handling individual PJ_GRIDINFO's. Includes - * loaders for all formats but CTABLE (in nad_init.c). - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -/************************************************************************/ -/* swap_words() */ -/* */ -/* Convert the byte order of the given word(s) in place. */ -/************************************************************************/ - -static const int byte_order_test = 1; -#define IS_LSB (1 == ((const unsigned char *) (&byte_order_test))[0]) - -static void swap_words( unsigned char *data, int word_size, int word_count ) - -{ - int word; - - for( word = 0; word < word_count; word++ ) - { - int i; - - for( i = 0; i < word_size/2; i++ ) - { - unsigned char t; - - t = data[i]; - data[i] = data[word_size-i-1]; - data[word_size-i-1] = t; - } - - data += word_size; - } -} - -/************************************************************************/ -/* to_double() */ -/* */ -/* Returns a double from the pointed data. */ -/************************************************************************/ - -static double to_double( unsigned char* data ) -{ - double d; - memcpy(&d, data, sizeof(d)); - return d; -} - -/************************************************************************/ -/* pj_gridinfo_free() */ -/************************************************************************/ - -void pj_gridinfo_free( projCtx ctx, PJ_GRIDINFO *gi ) - -{ - if( gi == NULL ) - return; - - if( gi->child != NULL ) - { - PJ_GRIDINFO *child, *next; - - for( child = gi->child; child != NULL; child=next) - { - next=child->next; - pj_gridinfo_free( ctx, child ); - } - } - - if( gi->ct != NULL ) - nad_free( gi->ct ); - - free( gi->gridname ); - if( gi->filename != NULL ) - free( gi->filename ); - - pj_dalloc( gi ); -} - -/************************************************************************/ -/* pj_gridinfo_load() */ -/* */ -/* This function is intended to implement delayed loading of */ -/* the data contents of a grid file. The header and related */ -/* stuff are loaded by pj_gridinfo_init(). */ -/************************************************************************/ - -int pj_gridinfo_load( projCtx ctx, PJ_GRIDINFO *gi ) - -{ - struct CTABLE ct_tmp; - - if( gi == NULL || gi->ct == NULL ) - return 0; - - pj_acquire_lock(); - if( gi->ct->cvs != NULL ) - { - pj_release_lock(); - return 1; - } - - memcpy(&ct_tmp, gi->ct, sizeof(struct CTABLE)); - -/* -------------------------------------------------------------------- */ -/* Original platform specific CTable format. */ -/* -------------------------------------------------------------------- */ - if( strcmp(gi->format,"ctable") == 0 ) - { - PAFile fid; - int result; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == NULL ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - result = nad_ctable_load( ctx, &ct_tmp, fid ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - - return result; - } - -/* -------------------------------------------------------------------- */ -/* CTable2 format. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ctable2") == 0 ) - { - PAFile fid; - int result; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == NULL ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - result = nad_ctable2_load( ctx, &ct_tmp, fid ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - - pj_release_lock(); - return result; - } - -/* -------------------------------------------------------------------- */ -/* NTv1 format. */ -/* We process one line at a time. Note that the array storage */ -/* direction (e-w) is different in the NTv1 file and what */ -/* the CTABLE is supposed to have. The phi/lam are also */ -/* reversed, and we have to be aware of byte swapping. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ntv1") == 0 ) - { - double *row_buf; - int row; - PAFile fid; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == NULL ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - row_buf = (double *) pj_malloc(gi->ct->lim.lam * sizeof(double) * 2); - ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); - if( row_buf == NULL || ct_tmp.cvs == NULL ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - for( row = 0; row < gi->ct->lim.phi; row++ ) - { - int i; - FLP *cvs; - double *diff_seconds; - - if( pj_ctx_fread( ctx, row_buf, - sizeof(double), gi->ct->lim.lam * 2, fid ) - != (size_t)( 2 * gi->ct->lim.lam ) ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( IS_LSB ) - swap_words( (unsigned char *) row_buf, 8, gi->ct->lim.lam*2 ); - - /* convert seconds to radians */ - diff_seconds = row_buf; - - for( i = 0; i < gi->ct->lim.lam; i++ ) - { - cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam - + (gi->ct->lim.lam - i - 1); - - cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - } - } - - pj_dalloc( row_buf ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - - return 1; - } - -/* -------------------------------------------------------------------- */ -/* NTv2 format. */ -/* We process one line at a time. Note that the array storage */ -/* direction (e-w) is different in the NTv2 file and what */ -/* the CTABLE is supposed to have. The phi/lam are also */ -/* reversed, and we have to be aware of byte swapping. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"ntv2") == 0 ) - { - float *row_buf; - int row; - PAFile fid; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv2 - loading grid %s", gi->ct->id ); - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == NULL ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - row_buf = (float *) pj_malloc(gi->ct->lim.lam * sizeof(float) * 4); - ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); - if( row_buf == NULL || ct_tmp.cvs == NULL ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - for( row = 0; row < gi->ct->lim.phi; row++ ) - { - int i; - FLP *cvs; - float *diff_seconds; - - if( pj_ctx_fread( ctx, row_buf, sizeof(float), - gi->ct->lim.lam*4, fid ) - != (size_t)( 4 * gi->ct->lim.lam ) ) - { - pj_dalloc( row_buf ); - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( gi->must_swap ) - swap_words( (unsigned char *) row_buf, 4, - gi->ct->lim.lam*4 ); - - /* convert seconds to radians */ - diff_seconds = row_buf; - - for( i = 0; i < gi->ct->lim.lam; i++ ) - { - cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam - + (gi->ct->lim.lam - i - 1); - - cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); - diff_seconds += 2; /* skip accuracy values */ - } - } - - pj_dalloc( row_buf ); - - pj_ctx_fclose( ctx, fid ); - - gi->ct->cvs = ct_tmp.cvs; - - pj_release_lock(); - return 1; - } - -/* -------------------------------------------------------------------- */ -/* GTX format. */ -/* -------------------------------------------------------------------- */ - else if( strcmp(gi->format,"gtx") == 0 ) - { - int words = gi->ct->lim.lam * gi->ct->lim.phi; - PAFile fid; - - fid = pj_open_lib( ctx, gi->filename, "rb" ); - - if( fid == NULL ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); - - ct_tmp.cvs = (FLP *) pj_malloc(words*sizeof(float)); - if( ct_tmp.cvs == NULL ) - { - pj_ctx_set_errno( ctx, ENOMEM ); - pj_release_lock(); - return 0; - } - - if( pj_ctx_fread( ctx, ct_tmp.cvs, sizeof(float), words, fid ) - != (size_t)words ) - { - pj_dalloc( ct_tmp.cvs ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return 0; - } - - if( IS_LSB ) - swap_words( (unsigned char *) ct_tmp.cvs, 4, words ); - - pj_ctx_fclose( ctx, fid ); - gi->ct->cvs = ct_tmp.cvs; - pj_release_lock(); - return 1; - } - - else - { - pj_release_lock(); - return 0; - } -} - -/************************************************************************/ -/* gridinfo_parent() */ -/* */ -/* Seek a parent grid file by name from a grid list */ -/************************************************************************/ - -static PJ_GRIDINFO* gridinfo_parent( PJ_GRIDINFO *gilist, - const char *name, int length ) -{ - while( gilist ) - { - if( strncmp(gilist->ct->id,name,length) == 0 ) return gilist; - if( gilist->child ) - { - PJ_GRIDINFO *parent=gridinfo_parent( gilist->child, name, length ); - if( parent ) return parent; - } - gilist=gilist->next; - } - return gilist; -} - -/************************************************************************/ -/* pj_gridinfo_init_ntv2() */ -/* */ -/* Load a ntv2 (.gsb) file. */ -/************************************************************************/ - -static int pj_gridinfo_init_ntv2( projCtx ctx, PAFile fid, PJ_GRIDINFO *gilist ) -{ - unsigned char header[11*16]; - int num_subfiles, subfile; - int must_swap; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the overview header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( header[8] == 11 ) - must_swap = !IS_LSB; - else - must_swap = IS_LSB; - -/* -------------------------------------------------------------------- */ -/* Byte swap interesting fields if needed. */ -/* -------------------------------------------------------------------- */ - if( must_swap ) - { - swap_words( header+8, 4, 1 ); - swap_words( header+8+16, 4, 1 ); - swap_words( header+8+32, 4, 1 ); - swap_words( header+8+7*16, 8, 1 ); - swap_words( header+8+8*16, 8, 1 ); - swap_words( header+8+9*16, 8, 1 ); - swap_words( header+8+10*16, 8, 1 ); - } - -/* -------------------------------------------------------------------- */ -/* Get the subfile count out ... all we really use for now. */ -/* -------------------------------------------------------------------- */ - memcpy( &num_subfiles, header+8+32, 4 ); - -/* ==================================================================== */ -/* Step through the subfiles, creating a PJ_GRIDINFO for each. */ -/* ==================================================================== */ - for( subfile = 0; subfile < num_subfiles; subfile++ ) - { - struct CTABLE *ct; - LP ur; - int gs_count; - PJ_GRIDINFO *gi; - -/* -------------------------------------------------------------------- */ -/* Read header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - if( strncmp((const char *) header,"SUB_NAME",8) != 0 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Byte swap interesting fields if needed. */ -/* -------------------------------------------------------------------- */ - if( must_swap ) - { - swap_words( header+8+16*4, 8, 1 ); - swap_words( header+8+16*5, 8, 1 ); - swap_words( header+8+16*6, 8, 1 ); - swap_words( header+8+16*7, 8, 1 ); - swap_words( header+8+16*8, 8, 1 ); - swap_words( header+8+16*9, 8, 1 ); - swap_words( header+8+16*10, 4, 1 ); - } - -/* -------------------------------------------------------------------- */ -/* Initialize a corresponding "ct" structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strncpy( ct->id, (const char *) header + 8, 8 ); - ct->id[8] = '\0'; - - ct->ll.lam = - to_double(header+7*16+8); /* W_LONG */ - ct->ll.phi = to_double(header+4*16+8); /* S_LAT */ - - ur.lam = - to_double(header+6*16+8); /* E_LONG */ - ur.phi = to_double(header+5*16+8); /* N_LAT */ - - ct->del.lam = to_double(header+9*16+8); - ct->del.phi = to_double(header+8*16+8); - - ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; - ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam/3600.0, ct->ll.phi/3600.0, - ur.lam/3600.0, ur.phi/3600.0 ); - - ct->ll.lam *= DEG_TO_RAD/3600.0; - ct->ll.phi *= DEG_TO_RAD/3600.0; - ct->del.lam *= DEG_TO_RAD/3600.0; - ct->del.phi *= DEG_TO_RAD/3600.0; - - memcpy( &gs_count, header + 8 + 16*10, 4 ); - if( gs_count != ct->lim.lam * ct->lim.phi ) - { - pj_log( ctx, PJ_LOG_ERROR, - "GS_COUNT(%d) does not match expected cells (%dx%d=%d)", - gs_count, ct->lim.lam, ct->lim.phi, - ct->lim.lam * ct->lim.phi ); - pj_dalloc(ct); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - - ct->cvs = NULL; - -/* -------------------------------------------------------------------- */ -/* Create a new gridinfo for this if we aren't processing the */ -/* 1st subfile, and initialize our grid info. */ -/* -------------------------------------------------------------------- */ - if( subfile == 0 ) - gi = gilist; - else - { - gi = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); - if (!gi) { - pj_dalloc(ct); - pj_gridinfo_free(ctx, gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - - gi->gridname = pj_strdup( gilist->gridname ); - gi->filename = pj_strdup( gilist->filename ); - if (!gi->gridname || !gi->filename) { - pj_gridinfo_free(ctx, gi); - pj_dalloc(ct); - pj_gridinfo_free(ctx, gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - gi->next = NULL; - } - - gi->must_swap = must_swap; - gi->ct = ct; - gi->format = "ntv2"; - gi->grid_offset = pj_ctx_ftell( ctx, fid ); - -/* -------------------------------------------------------------------- */ -/* Attach to the correct list or sublist. */ -/* -------------------------------------------------------------------- */ - if( strncmp((const char *)header+24,"NONE",4) == 0 ) - { - if( gi != gilist ) - { - PJ_GRIDINFO *lnk; - - for( lnk = gilist; lnk->next != NULL; lnk = lnk->next ) {} - lnk->next = gi; - } - } - - else - { - PJ_GRIDINFO *lnk; - PJ_GRIDINFO *gp = gridinfo_parent(gilist, - (const char*)header+24,8); - - if( gp == NULL ) - { - pj_log( ctx, PJ_LOG_ERROR, - "pj_gridinfo_init_ntv2(): " - "failed to find parent %8.8s for %s.", - (const char *) header+24, gi->ct->id ); - - for( lnk = gilist; lnk->next != NULL; lnk = lnk->next ) {} - lnk->next = gi; - } - else - { - if( gp->child == NULL ) - { - gp->child = gi; - } - else - { - for( lnk = gp->child; lnk->next != NULL; lnk = lnk->next ) {} - lnk->next = gi; - } - } - } - -/* -------------------------------------------------------------------- */ -/* Seek past the data. */ -/* -------------------------------------------------------------------- */ - pj_ctx_fseek( ctx, fid, gs_count * 16, SEEK_CUR ); - } - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init_ntv1() */ -/* */ -/* Load an NTv1 style Canadian grid shift file. */ -/************************************************************************/ - -static int pj_gridinfo_init_ntv1( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) - -{ - unsigned char header[192]; /* 12 records of 16 bytes */ - struct CTABLE *ct; - LP ur; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Regularize fields of interest. */ -/* -------------------------------------------------------------------- */ - if( IS_LSB ) - { - swap_words( header+8, 4, 1 ); - swap_words( header+24, 8, 1 ); - swap_words( header+40, 8, 1 ); - swap_words( header+56, 8, 1 ); - swap_words( header+72, 8, 1 ); - swap_words( header+88, 8, 1 ); - swap_words( header+104, 8, 1 ); - } - - if( *((int *) (header+8)) != 12 ) - { - pj_log( ctx, PJ_LOG_ERROR, - "NTv1 grid shift file has wrong record count, corrupt?" ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Fill in CTABLE structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strcpy( ct->id, "NTv1 Grid Shift File" ); - - ct->ll.lam = - to_double(header+72); - ct->ll.phi = to_double(header+24); - ur.lam = - to_double(header+56); - ur.phi = to_double(header+40); - ct->del.lam = to_double(header+104); - ct->del.phi = to_double(header+88); - ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; - ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "NTv1 %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->lim.lam, ct->lim.phi, - ct->ll.lam, ct->ll.phi, ur.lam, ur.phi ); - - ct->ll.lam *= DEG_TO_RAD; - ct->ll.phi *= DEG_TO_RAD; - ct->del.lam *= DEG_TO_RAD; - ct->del.phi *= DEG_TO_RAD; - ct->cvs = NULL; - - gi->ct = ct; - gi->grid_offset = (long) sizeof(header); - gi->format = "ntv1"; - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init_gtx() */ -/* */ -/* Load a NOAA .gtx vertical datum shift file. */ -/************************************************************************/ - -static int pj_gridinfo_init_gtx( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) - -{ - unsigned char header[40]; - struct CTABLE *ct; - double xorigin,yorigin,xstep,ystep; - int rows, columns; - - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(pj_int32) == 4 ); - /* cppcheck-suppress sizeofCalculation */ - STATIC_ASSERT( sizeof(double) == 8 ); - -/* -------------------------------------------------------------------- */ -/* Read the header. */ -/* -------------------------------------------------------------------- */ - if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) - { - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Regularize fields of interest and extract. */ -/* -------------------------------------------------------------------- */ - if( IS_LSB ) - { - swap_words( header+0, 8, 4 ); - swap_words( header+32, 4, 2 ); - } - - memcpy( &yorigin, header+0, 8 ); - memcpy( &xorigin, header+8, 8 ); - memcpy( &ystep, header+16, 8 ); - memcpy( &xstep, header+24, 8 ); - - memcpy( &rows, header+32, 4 ); - memcpy( &columns, header+36, 4 ); - - if( xorigin < -360 || xorigin > 360 - || yorigin < -90 || yorigin > 90 ) - { - pj_log( ctx, PJ_LOG_ERROR, - "gtx file header has invalid extents, corrupt?"); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - return 0; - } - -/* -------------------------------------------------------------------- */ -/* Fill in CTABLE structure. */ -/* -------------------------------------------------------------------- */ - ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); - if (!ct) { - pj_ctx_set_errno(ctx, ENOMEM); - return 0; - } - strcpy( ct->id, "GTX Vertical Grid Shift File" ); - - ct->ll.lam = xorigin; - ct->ll.phi = yorigin; - ct->del.lam = xstep; - ct->del.phi = ystep; - ct->lim.lam = columns; - ct->lim.phi = rows; - - /* some GTX files come in 0-360 and we shift them back into the - expected -180 to 180 range if possible. This does not solve - problems with grids spanning the dateline. */ - if( ct->ll.lam >= 180.0 ) - ct->ll.lam -= 360.0; - - if( ct->ll.lam >= 0.0 && ct->ll.lam + ct->del.lam * ct->lim.lam > 180.0 ) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "This GTX spans the dateline! This will cause problems." ); - } - - pj_log( ctx, PJ_LOG_DEBUG_MINOR, - "GTX %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->lim.lam, ct->lim.phi, - ct->ll.lam, ct->ll.phi, - ct->ll.lam + (columns-1)*xstep, ct->ll.phi + (rows-1)*ystep); - - ct->ll.lam *= DEG_TO_RAD; - ct->ll.phi *= DEG_TO_RAD; - ct->del.lam *= DEG_TO_RAD; - ct->del.phi *= DEG_TO_RAD; - ct->cvs = NULL; - - gi->ct = ct; - gi->grid_offset = 40; - gi->format = "gtx"; - - return 1; -} - -/************************************************************************/ -/* pj_gridinfo_init() */ -/* */ -/* Open and parse header details from a datum gridshift file */ -/* returning a list of PJ_GRIDINFOs for the grids in that */ -/* file. This superceeds use of nad_init() for modern */ -/* applications. */ -/************************************************************************/ - -PJ_GRIDINFO *pj_gridinfo_init( projCtx ctx, const char *gridname ) - -{ - char fname[MAX_PATH_FILENAME+1]; - PJ_GRIDINFO *gilist; - PAFile fp; - char header[160]; - size_t header_size = 0; - - errno = pj_errno = 0; - ctx->last_errno = 0; - -/* -------------------------------------------------------------------- */ -/* Initialize a GRIDINFO with stub info we would use if it */ -/* cannot be loaded. */ -/* -------------------------------------------------------------------- */ - gilist = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); - if (!gilist) { - pj_ctx_set_errno(ctx, ENOMEM); - return NULL; - } - - gilist->gridname = pj_strdup( gridname ); - if (!gilist->gridname) { - pj_dalloc(gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return NULL; - } - gilist->filename = NULL; - gilist->format = "missing"; - gilist->grid_offset = 0; - gilist->ct = NULL; - gilist->next = NULL; - -/* -------------------------------------------------------------------- */ -/* Open the file using the usual search rules. */ -/* -------------------------------------------------------------------- */ - strcpy(fname, gridname); - if (!(fp = pj_open_lib(ctx, fname, "rb"))) { - ctx->last_errno = 0; /* don't treat as a persistent error */ - return gilist; - } - - gilist->filename = pj_strdup(fname); - if (!gilist->filename) { - pj_dalloc(gilist->gridname); - pj_dalloc(gilist); - pj_ctx_set_errno(ctx, ENOMEM); - return NULL; - } - -/* -------------------------------------------------------------------- */ -/* Load a header, to determine the file type. */ -/* -------------------------------------------------------------------- */ - if( (header_size = pj_ctx_fread( ctx, header, 1, - sizeof(header), fp ) ) != sizeof(header) ) - { - /* some files may be smaller that sizeof(header), eg 160, so */ - ctx->last_errno = 0; /* don't treat as a persistent error */ - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "pj_gridinfo_init: short header read of %d bytes", - (int)header_size ); - } - - pj_ctx_fseek( ctx, fp, SEEK_SET, 0 ); - -/* -------------------------------------------------------------------- */ -/* Determine file type. */ -/* -------------------------------------------------------------------- */ - if( header_size >= 144 + 16 - && strncmp(header + 0, "HEADER", 6) == 0 - && strncmp(header + 96, "W GRID", 6) == 0 - && strncmp(header + 144, "TO NAD83 ", 16) == 0 ) - { - pj_gridinfo_init_ntv1( ctx, fp, gilist ); - } - - else if( header_size >= 48 + 7 - && strncmp(header + 0, "NUM_OREC", 8) == 0 - && strncmp(header + 48, "GS_TYPE", 7) == 0 ) - { - pj_gridinfo_init_ntv2( ctx, fp, gilist ); - } - - else if( strlen(gridname) > 4 - && (strcmp(gridname+strlen(gridname)-3,"gtx") == 0 - || strcmp(gridname+strlen(gridname)-3,"GTX") == 0) ) - { - pj_gridinfo_init_gtx( ctx, fp, gilist ); - } - - else if( header_size >= 9 && strncmp(header + 0,"CTABLE V2",9) == 0 ) - { - struct CTABLE *ct = nad_ctable2_init( ctx, fp ); - - gilist->format = "ctable2"; - gilist->ct = ct; - - if (ct == NULL) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "CTABLE V2 ct is NULL."); - } - else - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "Ctable2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, - (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, - (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); - } - } - - else - { - struct CTABLE *ct = nad_ctable_init( ctx, fp ); - if (ct == NULL) - { - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "CTABLE ct is NULL."); - } else - { - gilist->format = "ctable"; - gilist->ct = ct; - - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "Ctable %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", - ct->id, - ct->lim.lam, ct->lim.phi, - ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, - (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, - (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); - } - } - - pj_ctx_fclose(ctx, fp); - - return gilist; -} diff --git a/src/pj_gridinfo.cpp b/src/pj_gridinfo.cpp new file mode 100644 index 00000000..de0e8d31 --- /dev/null +++ b/src/pj_gridinfo.cpp @@ -0,0 +1,991 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Functions for handling individual PJ_GRIDINFO's. Includes + * loaders for all formats but CTABLE (in nad_init.c). + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +/************************************************************************/ +/* swap_words() */ +/* */ +/* Convert the byte order of the given word(s) in place. */ +/************************************************************************/ + +static const int byte_order_test = 1; +#define IS_LSB (1 == ((const unsigned char *) (&byte_order_test))[0]) + +static void swap_words( unsigned char *data, int word_size, int word_count ) + +{ + int word; + + for( word = 0; word < word_count; word++ ) + { + int i; + + for( i = 0; i < word_size/2; i++ ) + { + unsigned char t; + + t = data[i]; + data[i] = data[word_size-i-1]; + data[word_size-i-1] = t; + } + + data += word_size; + } +} + +/************************************************************************/ +/* to_double() */ +/* */ +/* Returns a double from the pointed data. */ +/************************************************************************/ + +static double to_double( unsigned char* data ) +{ + double d; + memcpy(&d, data, sizeof(d)); + return d; +} + +/************************************************************************/ +/* pj_gridinfo_free() */ +/************************************************************************/ + +void pj_gridinfo_free( projCtx ctx, PJ_GRIDINFO *gi ) + +{ + if( gi == NULL ) + return; + + if( gi->child != NULL ) + { + PJ_GRIDINFO *child, *next; + + for( child = gi->child; child != NULL; child=next) + { + next=child->next; + pj_gridinfo_free( ctx, child ); + } + } + + if( gi->ct != NULL ) + nad_free( gi->ct ); + + free( gi->gridname ); + if( gi->filename != NULL ) + free( gi->filename ); + + pj_dalloc( gi ); +} + +/************************************************************************/ +/* pj_gridinfo_load() */ +/* */ +/* This function is intended to implement delayed loading of */ +/* the data contents of a grid file. The header and related */ +/* stuff are loaded by pj_gridinfo_init(). */ +/************************************************************************/ + +int pj_gridinfo_load( projCtx ctx, PJ_GRIDINFO *gi ) + +{ + struct CTABLE ct_tmp; + + if( gi == NULL || gi->ct == NULL ) + return 0; + + pj_acquire_lock(); + if( gi->ct->cvs != NULL ) + { + pj_release_lock(); + return 1; + } + + memcpy(&ct_tmp, gi->ct, sizeof(struct CTABLE)); + +/* -------------------------------------------------------------------- */ +/* Original platform specific CTable format. */ +/* -------------------------------------------------------------------- */ + if( strcmp(gi->format,"ctable") == 0 ) + { + PAFile fid; + int result; + + fid = pj_open_lib( ctx, gi->filename, "rb" ); + + if( fid == NULL ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + result = nad_ctable_load( ctx, &ct_tmp, fid ); + + pj_ctx_fclose( ctx, fid ); + + gi->ct->cvs = ct_tmp.cvs; + pj_release_lock(); + + return result; + } + +/* -------------------------------------------------------------------- */ +/* CTable2 format. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(gi->format,"ctable2") == 0 ) + { + PAFile fid; + int result; + + fid = pj_open_lib( ctx, gi->filename, "rb" ); + + if( fid == NULL ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + result = nad_ctable2_load( ctx, &ct_tmp, fid ); + + pj_ctx_fclose( ctx, fid ); + + gi->ct->cvs = ct_tmp.cvs; + + pj_release_lock(); + return result; + } + +/* -------------------------------------------------------------------- */ +/* NTv1 format. */ +/* We process one line at a time. Note that the array storage */ +/* direction (e-w) is different in the NTv1 file and what */ +/* the CTABLE is supposed to have. The phi/lam are also */ +/* reversed, and we have to be aware of byte swapping. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(gi->format,"ntv1") == 0 ) + { + double *row_buf; + int row; + PAFile fid; + + fid = pj_open_lib( ctx, gi->filename, "rb" ); + + if( fid == NULL ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); + + row_buf = (double *) pj_malloc(gi->ct->lim.lam * sizeof(double) * 2); + ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); + if( row_buf == NULL || ct_tmp.cvs == NULL ) + { + pj_dalloc( row_buf ); + pj_dalloc( ct_tmp.cvs ); + pj_ctx_set_errno( ctx, ENOMEM ); + pj_release_lock(); + return 0; + } + + for( row = 0; row < gi->ct->lim.phi; row++ ) + { + int i; + FLP *cvs; + double *diff_seconds; + + if( pj_ctx_fread( ctx, row_buf, + sizeof(double), gi->ct->lim.lam * 2, fid ) + != (size_t)( 2 * gi->ct->lim.lam ) ) + { + pj_dalloc( row_buf ); + pj_dalloc( ct_tmp.cvs ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + if( IS_LSB ) + swap_words( (unsigned char *) row_buf, 8, gi->ct->lim.lam*2 ); + + /* convert seconds to radians */ + diff_seconds = row_buf; + + for( i = 0; i < gi->ct->lim.lam; i++ ) + { + cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam + + (gi->ct->lim.lam - i - 1); + + cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); + cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); + } + } + + pj_dalloc( row_buf ); + + pj_ctx_fclose( ctx, fid ); + + gi->ct->cvs = ct_tmp.cvs; + pj_release_lock(); + + return 1; + } + +/* -------------------------------------------------------------------- */ +/* NTv2 format. */ +/* We process one line at a time. Note that the array storage */ +/* direction (e-w) is different in the NTv2 file and what */ +/* the CTABLE is supposed to have. The phi/lam are also */ +/* reversed, and we have to be aware of byte swapping. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(gi->format,"ntv2") == 0 ) + { + float *row_buf; + int row; + PAFile fid; + + pj_log( ctx, PJ_LOG_DEBUG_MINOR, + "NTv2 - loading grid %s", gi->ct->id ); + + fid = pj_open_lib( ctx, gi->filename, "rb" ); + + if( fid == NULL ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); + + row_buf = (float *) pj_malloc(gi->ct->lim.lam * sizeof(float) * 4); + ct_tmp.cvs = (FLP *) pj_malloc(gi->ct->lim.lam*gi->ct->lim.phi*sizeof(FLP)); + if( row_buf == NULL || ct_tmp.cvs == NULL ) + { + pj_dalloc( row_buf ); + pj_dalloc( ct_tmp.cvs ); + pj_ctx_set_errno( ctx, ENOMEM ); + pj_release_lock(); + return 0; + } + + for( row = 0; row < gi->ct->lim.phi; row++ ) + { + int i; + FLP *cvs; + float *diff_seconds; + + if( pj_ctx_fread( ctx, row_buf, sizeof(float), + gi->ct->lim.lam*4, fid ) + != (size_t)( 4 * gi->ct->lim.lam ) ) + { + pj_dalloc( row_buf ); + pj_dalloc( ct_tmp.cvs ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + if( gi->must_swap ) + swap_words( (unsigned char *) row_buf, 4, + gi->ct->lim.lam*4 ); + + /* convert seconds to radians */ + diff_seconds = row_buf; + + for( i = 0; i < gi->ct->lim.lam; i++ ) + { + cvs = ct_tmp.cvs + (row) * gi->ct->lim.lam + + (gi->ct->lim.lam - i - 1); + + cvs->phi = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); + cvs->lam = (float) (*(diff_seconds++) * ((M_PI/180.0) / 3600.0)); + diff_seconds += 2; /* skip accuracy values */ + } + } + + pj_dalloc( row_buf ); + + pj_ctx_fclose( ctx, fid ); + + gi->ct->cvs = ct_tmp.cvs; + + pj_release_lock(); + return 1; + } + +/* -------------------------------------------------------------------- */ +/* GTX format. */ +/* -------------------------------------------------------------------- */ + else if( strcmp(gi->format,"gtx") == 0 ) + { + int words = gi->ct->lim.lam * gi->ct->lim.phi; + PAFile fid; + + fid = pj_open_lib( ctx, gi->filename, "rb" ); + + if( fid == NULL ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + pj_ctx_fseek( ctx, fid, gi->grid_offset, SEEK_SET ); + + ct_tmp.cvs = (FLP *) pj_malloc(words*sizeof(float)); + if( ct_tmp.cvs == NULL ) + { + pj_ctx_set_errno( ctx, ENOMEM ); + pj_release_lock(); + return 0; + } + + if( pj_ctx_fread( ctx, ct_tmp.cvs, sizeof(float), words, fid ) + != (size_t)words ) + { + pj_dalloc( ct_tmp.cvs ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return 0; + } + + if( IS_LSB ) + swap_words( (unsigned char *) ct_tmp.cvs, 4, words ); + + pj_ctx_fclose( ctx, fid ); + gi->ct->cvs = ct_tmp.cvs; + pj_release_lock(); + return 1; + } + + else + { + pj_release_lock(); + return 0; + } +} + +/************************************************************************/ +/* gridinfo_parent() */ +/* */ +/* Seek a parent grid file by name from a grid list */ +/************************************************************************/ + +static PJ_GRIDINFO* gridinfo_parent( PJ_GRIDINFO *gilist, + const char *name, int length ) +{ + while( gilist ) + { + if( strncmp(gilist->ct->id,name,length) == 0 ) return gilist; + if( gilist->child ) + { + PJ_GRIDINFO *parent=gridinfo_parent( gilist->child, name, length ); + if( parent ) return parent; + } + gilist=gilist->next; + } + return gilist; +} + +/************************************************************************/ +/* pj_gridinfo_init_ntv2() */ +/* */ +/* Load a ntv2 (.gsb) file. */ +/************************************************************************/ + +static int pj_gridinfo_init_ntv2( projCtx ctx, PAFile fid, PJ_GRIDINFO *gilist ) +{ + unsigned char header[11*16]; + int num_subfiles, subfile; + int must_swap; + + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(pj_int32) == 4 ); + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(double) == 8 ); + +/* -------------------------------------------------------------------- */ +/* Read the overview header. */ +/* -------------------------------------------------------------------- */ + if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + + if( header[8] == 11 ) + must_swap = !IS_LSB; + else + must_swap = IS_LSB; + +/* -------------------------------------------------------------------- */ +/* Byte swap interesting fields if needed. */ +/* -------------------------------------------------------------------- */ + if( must_swap ) + { + swap_words( header+8, 4, 1 ); + swap_words( header+8+16, 4, 1 ); + swap_words( header+8+32, 4, 1 ); + swap_words( header+8+7*16, 8, 1 ); + swap_words( header+8+8*16, 8, 1 ); + swap_words( header+8+9*16, 8, 1 ); + swap_words( header+8+10*16, 8, 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Get the subfile count out ... all we really use for now. */ +/* -------------------------------------------------------------------- */ + memcpy( &num_subfiles, header+8+32, 4 ); + +/* ==================================================================== */ +/* Step through the subfiles, creating a PJ_GRIDINFO for each. */ +/* ==================================================================== */ + for( subfile = 0; subfile < num_subfiles; subfile++ ) + { + struct CTABLE *ct; + LP ur; + int gs_count; + PJ_GRIDINFO *gi; + +/* -------------------------------------------------------------------- */ +/* Read header. */ +/* -------------------------------------------------------------------- */ + if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + + if( strncmp((const char *) header,"SUB_NAME",8) != 0 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Byte swap interesting fields if needed. */ +/* -------------------------------------------------------------------- */ + if( must_swap ) + { + swap_words( header+8+16*4, 8, 1 ); + swap_words( header+8+16*5, 8, 1 ); + swap_words( header+8+16*6, 8, 1 ); + swap_words( header+8+16*7, 8, 1 ); + swap_words( header+8+16*8, 8, 1 ); + swap_words( header+8+16*9, 8, 1 ); + swap_words( header+8+16*10, 4, 1 ); + } + +/* -------------------------------------------------------------------- */ +/* Initialize a corresponding "ct" structure. */ +/* -------------------------------------------------------------------- */ + ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); + if (!ct) { + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + strncpy( ct->id, (const char *) header + 8, 8 ); + ct->id[8] = '\0'; + + ct->ll.lam = - to_double(header+7*16+8); /* W_LONG */ + ct->ll.phi = to_double(header+4*16+8); /* S_LAT */ + + ur.lam = - to_double(header+6*16+8); /* E_LONG */ + ur.phi = to_double(header+5*16+8); /* N_LAT */ + + ct->del.lam = to_double(header+9*16+8); + ct->del.phi = to_double(header+8*16+8); + + ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; + ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; + + pj_log( ctx, PJ_LOG_DEBUG_MINOR, + "NTv2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", + ct->id, + ct->lim.lam, ct->lim.phi, + ct->ll.lam/3600.0, ct->ll.phi/3600.0, + ur.lam/3600.0, ur.phi/3600.0 ); + + ct->ll.lam *= DEG_TO_RAD/3600.0; + ct->ll.phi *= DEG_TO_RAD/3600.0; + ct->del.lam *= DEG_TO_RAD/3600.0; + ct->del.phi *= DEG_TO_RAD/3600.0; + + memcpy( &gs_count, header + 8 + 16*10, 4 ); + if( gs_count != ct->lim.lam * ct->lim.phi ) + { + pj_log( ctx, PJ_LOG_ERROR, + "GS_COUNT(%d) does not match expected cells (%dx%d=%d)", + gs_count, ct->lim.lam, ct->lim.phi, + ct->lim.lam * ct->lim.phi ); + pj_dalloc(ct); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + + ct->cvs = NULL; + +/* -------------------------------------------------------------------- */ +/* Create a new gridinfo for this if we aren't processing the */ +/* 1st subfile, and initialize our grid info. */ +/* -------------------------------------------------------------------- */ + if( subfile == 0 ) + gi = gilist; + else + { + gi = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); + if (!gi) { + pj_dalloc(ct); + pj_gridinfo_free(ctx, gilist); + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + + gi->gridname = pj_strdup( gilist->gridname ); + gi->filename = pj_strdup( gilist->filename ); + if (!gi->gridname || !gi->filename) { + pj_gridinfo_free(ctx, gi); + pj_dalloc(ct); + pj_gridinfo_free(ctx, gilist); + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + gi->next = NULL; + } + + gi->must_swap = must_swap; + gi->ct = ct; + gi->format = "ntv2"; + gi->grid_offset = pj_ctx_ftell( ctx, fid ); + +/* -------------------------------------------------------------------- */ +/* Attach to the correct list or sublist. */ +/* -------------------------------------------------------------------- */ + if( strncmp((const char *)header+24,"NONE",4) == 0 ) + { + if( gi != gilist ) + { + PJ_GRIDINFO *lnk; + + for( lnk = gilist; lnk->next != NULL; lnk = lnk->next ) {} + lnk->next = gi; + } + } + + else + { + PJ_GRIDINFO *lnk; + PJ_GRIDINFO *gp = gridinfo_parent(gilist, + (const char*)header+24,8); + + if( gp == NULL ) + { + pj_log( ctx, PJ_LOG_ERROR, + "pj_gridinfo_init_ntv2(): " + "failed to find parent %8.8s for %s.", + (const char *) header+24, gi->ct->id ); + + for( lnk = gilist; lnk->next != NULL; lnk = lnk->next ) {} + lnk->next = gi; + } + else + { + if( gp->child == NULL ) + { + gp->child = gi; + } + else + { + for( lnk = gp->child; lnk->next != NULL; lnk = lnk->next ) {} + lnk->next = gi; + } + } + } + +/* -------------------------------------------------------------------- */ +/* Seek past the data. */ +/* -------------------------------------------------------------------- */ + pj_ctx_fseek( ctx, fid, gs_count * 16, SEEK_CUR ); + } + + return 1; +} + +/************************************************************************/ +/* pj_gridinfo_init_ntv1() */ +/* */ +/* Load an NTv1 style Canadian grid shift file. */ +/************************************************************************/ + +static int pj_gridinfo_init_ntv1( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) + +{ + unsigned char header[192]; /* 12 records of 16 bytes */ + struct CTABLE *ct; + LP ur; + + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(pj_int32) == 4 ); + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(double) == 8 ); + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Regularize fields of interest. */ +/* -------------------------------------------------------------------- */ + if( IS_LSB ) + { + swap_words( header+8, 4, 1 ); + swap_words( header+24, 8, 1 ); + swap_words( header+40, 8, 1 ); + swap_words( header+56, 8, 1 ); + swap_words( header+72, 8, 1 ); + swap_words( header+88, 8, 1 ); + swap_words( header+104, 8, 1 ); + } + + if( *((int *) (header+8)) != 12 ) + { + pj_log( ctx, PJ_LOG_ERROR, + "NTv1 grid shift file has wrong record count, corrupt?" ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Fill in CTABLE structure. */ +/* -------------------------------------------------------------------- */ + ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); + if (!ct) { + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + strcpy( ct->id, "NTv1 Grid Shift File" ); + + ct->ll.lam = - to_double(header+72); + ct->ll.phi = to_double(header+24); + ur.lam = - to_double(header+56); + ur.phi = to_double(header+40); + ct->del.lam = to_double(header+104); + ct->del.phi = to_double(header+88); + ct->lim.lam = (pj_int32) (fabs(ur.lam-ct->ll.lam)/ct->del.lam + 0.5) + 1; + ct->lim.phi = (pj_int32) (fabs(ur.phi-ct->ll.phi)/ct->del.phi + 0.5) + 1; + + pj_log( ctx, PJ_LOG_DEBUG_MINOR, + "NTv1 %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", + ct->lim.lam, ct->lim.phi, + ct->ll.lam, ct->ll.phi, ur.lam, ur.phi ); + + ct->ll.lam *= DEG_TO_RAD; + ct->ll.phi *= DEG_TO_RAD; + ct->del.lam *= DEG_TO_RAD; + ct->del.phi *= DEG_TO_RAD; + ct->cvs = NULL; + + gi->ct = ct; + gi->grid_offset = (long) sizeof(header); + gi->format = "ntv1"; + + return 1; +} + +/************************************************************************/ +/* pj_gridinfo_init_gtx() */ +/* */ +/* Load a NOAA .gtx vertical datum shift file. */ +/************************************************************************/ + +static int pj_gridinfo_init_gtx( projCtx ctx, PAFile fid, PJ_GRIDINFO *gi ) + +{ + unsigned char header[40]; + struct CTABLE *ct; + double xorigin,yorigin,xstep,ystep; + int rows, columns; + + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(pj_int32) == 4 ); + /* cppcheck-suppress sizeofCalculation */ + STATIC_ASSERT( sizeof(double) == 8 ); + +/* -------------------------------------------------------------------- */ +/* Read the header. */ +/* -------------------------------------------------------------------- */ + if( pj_ctx_fread( ctx, header, sizeof(header), 1, fid ) != 1 ) + { + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Regularize fields of interest and extract. */ +/* -------------------------------------------------------------------- */ + if( IS_LSB ) + { + swap_words( header+0, 8, 4 ); + swap_words( header+32, 4, 2 ); + } + + memcpy( &yorigin, header+0, 8 ); + memcpy( &xorigin, header+8, 8 ); + memcpy( &ystep, header+16, 8 ); + memcpy( &xstep, header+24, 8 ); + + memcpy( &rows, header+32, 4 ); + memcpy( &columns, header+36, 4 ); + + if( xorigin < -360 || xorigin > 360 + || yorigin < -90 || yorigin > 90 ) + { + pj_log( ctx, PJ_LOG_ERROR, + "gtx file header has invalid extents, corrupt?"); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + return 0; + } + +/* -------------------------------------------------------------------- */ +/* Fill in CTABLE structure. */ +/* -------------------------------------------------------------------- */ + ct = (struct CTABLE *) pj_malloc(sizeof(struct CTABLE)); + if (!ct) { + pj_ctx_set_errno(ctx, ENOMEM); + return 0; + } + strcpy( ct->id, "GTX Vertical Grid Shift File" ); + + ct->ll.lam = xorigin; + ct->ll.phi = yorigin; + ct->del.lam = xstep; + ct->del.phi = ystep; + ct->lim.lam = columns; + ct->lim.phi = rows; + + /* some GTX files come in 0-360 and we shift them back into the + expected -180 to 180 range if possible. This does not solve + problems with grids spanning the dateline. */ + if( ct->ll.lam >= 180.0 ) + ct->ll.lam -= 360.0; + + if( ct->ll.lam >= 0.0 && ct->ll.lam + ct->del.lam * ct->lim.lam > 180.0 ) + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "This GTX spans the dateline! This will cause problems." ); + } + + pj_log( ctx, PJ_LOG_DEBUG_MINOR, + "GTX %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", + ct->lim.lam, ct->lim.phi, + ct->ll.lam, ct->ll.phi, + ct->ll.lam + (columns-1)*xstep, ct->ll.phi + (rows-1)*ystep); + + ct->ll.lam *= DEG_TO_RAD; + ct->ll.phi *= DEG_TO_RAD; + ct->del.lam *= DEG_TO_RAD; + ct->del.phi *= DEG_TO_RAD; + ct->cvs = NULL; + + gi->ct = ct; + gi->grid_offset = 40; + gi->format = "gtx"; + + return 1; +} + +/************************************************************************/ +/* pj_gridinfo_init() */ +/* */ +/* Open and parse header details from a datum gridshift file */ +/* returning a list of PJ_GRIDINFOs for the grids in that */ +/* file. This superceeds use of nad_init() for modern */ +/* applications. */ +/************************************************************************/ + +PJ_GRIDINFO *pj_gridinfo_init( projCtx ctx, const char *gridname ) + +{ + char fname[MAX_PATH_FILENAME+1]; + PJ_GRIDINFO *gilist; + PAFile fp; + char header[160]; + size_t header_size = 0; + + errno = pj_errno = 0; + ctx->last_errno = 0; + +/* -------------------------------------------------------------------- */ +/* Initialize a GRIDINFO with stub info we would use if it */ +/* cannot be loaded. */ +/* -------------------------------------------------------------------- */ + gilist = (PJ_GRIDINFO *) pj_calloc(1, sizeof(PJ_GRIDINFO)); + if (!gilist) { + pj_ctx_set_errno(ctx, ENOMEM); + return NULL; + } + + gilist->gridname = pj_strdup( gridname ); + if (!gilist->gridname) { + pj_dalloc(gilist); + pj_ctx_set_errno(ctx, ENOMEM); + return NULL; + } + gilist->filename = NULL; + gilist->format = "missing"; + gilist->grid_offset = 0; + gilist->ct = NULL; + gilist->next = NULL; + +/* -------------------------------------------------------------------- */ +/* Open the file using the usual search rules. */ +/* -------------------------------------------------------------------- */ + strcpy(fname, gridname); + if (!(fp = pj_open_lib(ctx, fname, "rb"))) { + ctx->last_errno = 0; /* don't treat as a persistent error */ + return gilist; + } + + gilist->filename = pj_strdup(fname); + if (!gilist->filename) { + pj_dalloc(gilist->gridname); + pj_dalloc(gilist); + pj_ctx_set_errno(ctx, ENOMEM); + return NULL; + } + +/* -------------------------------------------------------------------- */ +/* Load a header, to determine the file type. */ +/* -------------------------------------------------------------------- */ + if( (header_size = pj_ctx_fread( ctx, header, 1, + sizeof(header), fp ) ) != sizeof(header) ) + { + /* some files may be smaller that sizeof(header), eg 160, so */ + ctx->last_errno = 0; /* don't treat as a persistent error */ + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "pj_gridinfo_init: short header read of %d bytes", + (int)header_size ); + } + + pj_ctx_fseek( ctx, fp, SEEK_SET, 0 ); + +/* -------------------------------------------------------------------- */ +/* Determine file type. */ +/* -------------------------------------------------------------------- */ + if( header_size >= 144 + 16 + && strncmp(header + 0, "HEADER", 6) == 0 + && strncmp(header + 96, "W GRID", 6) == 0 + && strncmp(header + 144, "TO NAD83 ", 16) == 0 ) + { + pj_gridinfo_init_ntv1( ctx, fp, gilist ); + } + + else if( header_size >= 48 + 7 + && strncmp(header + 0, "NUM_OREC", 8) == 0 + && strncmp(header + 48, "GS_TYPE", 7) == 0 ) + { + pj_gridinfo_init_ntv2( ctx, fp, gilist ); + } + + else if( strlen(gridname) > 4 + && (strcmp(gridname+strlen(gridname)-3,"gtx") == 0 + || strcmp(gridname+strlen(gridname)-3,"GTX") == 0) ) + { + pj_gridinfo_init_gtx( ctx, fp, gilist ); + } + + else if( header_size >= 9 && strncmp(header + 0,"CTABLE V2",9) == 0 ) + { + struct CTABLE *ct = nad_ctable2_init( ctx, fp ); + + gilist->format = "ctable2"; + gilist->ct = ct; + + if (ct == NULL) + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "CTABLE V2 ct is NULL."); + } + else + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "Ctable2 %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", + ct->id, + ct->lim.lam, ct->lim.phi, + ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, + (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, + (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); + } + } + + else + { + struct CTABLE *ct = nad_ctable_init( ctx, fp ); + if (ct == NULL) + { + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "CTABLE ct is NULL."); + } else + { + gilist->format = "ctable"; + gilist->ct = ct; + + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "Ctable %s %dx%d: LL=(%.9g,%.9g) UR=(%.9g,%.9g)", + ct->id, + ct->lim.lam, ct->lim.phi, + ct->ll.lam * RAD_TO_DEG, ct->ll.phi * RAD_TO_DEG, + (ct->ll.lam + (ct->lim.lam-1)*ct->del.lam) * RAD_TO_DEG, + (ct->ll.phi + (ct->lim.phi-1)*ct->del.phi) * RAD_TO_DEG ); + } + } + + pj_ctx_fclose(ctx, fp); + + return gilist; +} diff --git a/src/pj_gridlist.c b/src/pj_gridlist.c deleted file mode 100644 index 332de8dd..00000000 --- a/src/pj_gridlist.c +++ /dev/null @@ -1,219 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Code to manage the list of currently loaded (cached) PJ_GRIDINFOs - * See pj_gridinfo.c for details of loading individual grids. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include - -#include "projects.h" - -static PJ_GRIDINFO *grid_list = NULL; -#define PJ_MAX_PATH_LENGTH 1024 - -/************************************************************************/ -/* pj_deallocate_grids() */ -/* */ -/* Deallocate all loaded grids. */ -/************************************************************************/ - -void pj_deallocate_grids() - -{ - while( grid_list != NULL ) - { - PJ_GRIDINFO *item = grid_list; - grid_list = grid_list->next; - item->next = NULL; - - pj_gridinfo_free( pj_get_default_ctx(), item ); - } -} - -/************************************************************************/ -/* pj_gridlist_merge_grid() */ -/* */ -/* Find/load the named gridfile and merge it into the */ -/* last_nadgrids_list. */ -/************************************************************************/ - -static int pj_gridlist_merge_gridfile( projCtx ctx, - const char *gridname, - PJ_GRIDINFO ***p_gridlist, - int *p_gridcount, - int *p_gridmax ) - -{ - int got_match=0; - PJ_GRIDINFO *this_grid, *tail = NULL; - -/* -------------------------------------------------------------------- */ -/* Try to find in the existing list of loaded grids. Add all */ -/* matching grids as with NTv2 we can get many grids from one */ -/* file (one shared gridname). */ -/* -------------------------------------------------------------------- */ - for( this_grid = grid_list; this_grid != NULL; this_grid = this_grid->next) - { - if( strcmp(this_grid->gridname,gridname) == 0 ) - { - got_match = 1; - - /* don't add to the list if it is invalid. */ - if( this_grid->ct == NULL ) - return 0; - - /* do we need to grow the list? */ - if( *p_gridcount >= *p_gridmax - 2 ) - { - PJ_GRIDINFO **new_list; - int new_max = *p_gridmax + 20; - - new_list = (PJ_GRIDINFO **) pj_calloc(new_max, sizeof(void *)); - if (!new_list) { - pj_ctx_set_errno( ctx, ENOMEM ); - return 0; - } - if( *p_gridlist != NULL ) - { - memcpy( new_list, *p_gridlist, - sizeof(void *) * (*p_gridmax) ); - pj_dalloc( *p_gridlist ); - } - - *p_gridlist = new_list; - *p_gridmax = new_max; - } - - /* add to the list */ - (*p_gridlist)[(*p_gridcount)++] = this_grid; - (*p_gridlist)[*p_gridcount] = NULL; - } - - tail = this_grid; - } - - if( got_match ) - return 1; - -/* -------------------------------------------------------------------- */ -/* Try to load the named grid. */ -/* -------------------------------------------------------------------- */ - this_grid = pj_gridinfo_init( ctx, gridname ); - - if( this_grid == NULL ) - { - return 0; - } - - if( tail != NULL ) - tail->next = this_grid; - else - grid_list = this_grid; - -/* -------------------------------------------------------------------- */ -/* Recurse to add the grid now that it is loaded. */ -/* -------------------------------------------------------------------- */ - return pj_gridlist_merge_gridfile( ctx, gridname, p_gridlist, - p_gridcount, p_gridmax ); -} - -/************************************************************************/ -/* pj_gridlist_from_nadgrids() */ -/* */ -/* This functions loads the list of grids corresponding to a */ -/* particular nadgrids string into a list, and returns it. The */ -/* list is kept around till a request is made with a different */ -/* string in order to cut down on the string parsing cost, and */ -/* the cost of building the list of tables each time. */ -/************************************************************************/ - -PJ_GRIDINFO **pj_gridlist_from_nadgrids( projCtx ctx, const char *nadgrids, - int *grid_count) - -{ - const char *s; - PJ_GRIDINFO **gridlist = NULL; - int grid_max = 0; - - pj_errno = 0; - *grid_count = 0; - - pj_acquire_lock(); - -/* -------------------------------------------------------------------- */ -/* Loop processing names out of nadgrids one at a time. */ -/* -------------------------------------------------------------------- */ - for( s = nadgrids; *s != '\0'; ) - { - size_t end_char; - int required = 1; - char name[PJ_MAX_PATH_LENGTH]; - - if( *s == '@' ) - { - required = 0; - s++; - } - - for( end_char = 0; - s[end_char] != '\0' && s[end_char] != ','; - end_char++ ) {} - - if( end_char >= sizeof(name) ) - { - pj_dalloc( gridlist ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return NULL; - } - - strncpy( name, s, end_char ); - name[end_char] = '\0'; - - s += end_char; - if( *s == ',' ) - s++; - - if( !pj_gridlist_merge_gridfile( ctx, name, &gridlist, grid_count, - &grid_max) - && required ) - { - pj_dalloc( gridlist ); - pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); - pj_release_lock(); - return NULL; - } - else - pj_errno = 0; - } - - pj_release_lock(); - - return gridlist; -} diff --git a/src/pj_gridlist.cpp b/src/pj_gridlist.cpp new file mode 100644 index 00000000..332de8dd --- /dev/null +++ b/src/pj_gridlist.cpp @@ -0,0 +1,219 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Code to manage the list of currently loaded (cached) PJ_GRIDINFOs + * See pj_gridinfo.c for details of loading individual grids. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include + +#include "projects.h" + +static PJ_GRIDINFO *grid_list = NULL; +#define PJ_MAX_PATH_LENGTH 1024 + +/************************************************************************/ +/* pj_deallocate_grids() */ +/* */ +/* Deallocate all loaded grids. */ +/************************************************************************/ + +void pj_deallocate_grids() + +{ + while( grid_list != NULL ) + { + PJ_GRIDINFO *item = grid_list; + grid_list = grid_list->next; + item->next = NULL; + + pj_gridinfo_free( pj_get_default_ctx(), item ); + } +} + +/************************************************************************/ +/* pj_gridlist_merge_grid() */ +/* */ +/* Find/load the named gridfile and merge it into the */ +/* last_nadgrids_list. */ +/************************************************************************/ + +static int pj_gridlist_merge_gridfile( projCtx ctx, + const char *gridname, + PJ_GRIDINFO ***p_gridlist, + int *p_gridcount, + int *p_gridmax ) + +{ + int got_match=0; + PJ_GRIDINFO *this_grid, *tail = NULL; + +/* -------------------------------------------------------------------- */ +/* Try to find in the existing list of loaded grids. Add all */ +/* matching grids as with NTv2 we can get many grids from one */ +/* file (one shared gridname). */ +/* -------------------------------------------------------------------- */ + for( this_grid = grid_list; this_grid != NULL; this_grid = this_grid->next) + { + if( strcmp(this_grid->gridname,gridname) == 0 ) + { + got_match = 1; + + /* don't add to the list if it is invalid. */ + if( this_grid->ct == NULL ) + return 0; + + /* do we need to grow the list? */ + if( *p_gridcount >= *p_gridmax - 2 ) + { + PJ_GRIDINFO **new_list; + int new_max = *p_gridmax + 20; + + new_list = (PJ_GRIDINFO **) pj_calloc(new_max, sizeof(void *)); + if (!new_list) { + pj_ctx_set_errno( ctx, ENOMEM ); + return 0; + } + if( *p_gridlist != NULL ) + { + memcpy( new_list, *p_gridlist, + sizeof(void *) * (*p_gridmax) ); + pj_dalloc( *p_gridlist ); + } + + *p_gridlist = new_list; + *p_gridmax = new_max; + } + + /* add to the list */ + (*p_gridlist)[(*p_gridcount)++] = this_grid; + (*p_gridlist)[*p_gridcount] = NULL; + } + + tail = this_grid; + } + + if( got_match ) + return 1; + +/* -------------------------------------------------------------------- */ +/* Try to load the named grid. */ +/* -------------------------------------------------------------------- */ + this_grid = pj_gridinfo_init( ctx, gridname ); + + if( this_grid == NULL ) + { + return 0; + } + + if( tail != NULL ) + tail->next = this_grid; + else + grid_list = this_grid; + +/* -------------------------------------------------------------------- */ +/* Recurse to add the grid now that it is loaded. */ +/* -------------------------------------------------------------------- */ + return pj_gridlist_merge_gridfile( ctx, gridname, p_gridlist, + p_gridcount, p_gridmax ); +} + +/************************************************************************/ +/* pj_gridlist_from_nadgrids() */ +/* */ +/* This functions loads the list of grids corresponding to a */ +/* particular nadgrids string into a list, and returns it. The */ +/* list is kept around till a request is made with a different */ +/* string in order to cut down on the string parsing cost, and */ +/* the cost of building the list of tables each time. */ +/************************************************************************/ + +PJ_GRIDINFO **pj_gridlist_from_nadgrids( projCtx ctx, const char *nadgrids, + int *grid_count) + +{ + const char *s; + PJ_GRIDINFO **gridlist = NULL; + int grid_max = 0; + + pj_errno = 0; + *grid_count = 0; + + pj_acquire_lock(); + +/* -------------------------------------------------------------------- */ +/* Loop processing names out of nadgrids one at a time. */ +/* -------------------------------------------------------------------- */ + for( s = nadgrids; *s != '\0'; ) + { + size_t end_char; + int required = 1; + char name[PJ_MAX_PATH_LENGTH]; + + if( *s == '@' ) + { + required = 0; + s++; + } + + for( end_char = 0; + s[end_char] != '\0' && s[end_char] != ','; + end_char++ ) {} + + if( end_char >= sizeof(name) ) + { + pj_dalloc( gridlist ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return NULL; + } + + strncpy( name, s, end_char ); + name[end_char] = '\0'; + + s += end_char; + if( *s == ',' ) + s++; + + if( !pj_gridlist_merge_gridfile( ctx, name, &gridlist, grid_count, + &grid_max) + && required ) + { + pj_dalloc( gridlist ); + pj_ctx_set_errno( ctx, PJD_ERR_FAILED_TO_LOAD_GRID ); + pj_release_lock(); + return NULL; + } + else + pj_errno = 0; + } + + pj_release_lock(); + + return gridlist; +} diff --git a/src/pj_init.c b/src/pj_init.c deleted file mode 100644 index 36a3276f..00000000 --- a/src/pj_init.c +++ /dev/null @@ -1,870 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Initialize projection object from string definition. Includes - * pj_init(), pj_init_plus() and pj_free() function. - * Author: Gerald Evenden, Frank Warmerdam - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * Copyright (c) 2002, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include -#include -#include - -#include "geodesic.h" -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - - -/**************************************************************************************/ -static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) { -/*************************************************************************************** - Convert a string (presumably originating from get_init_string) to a paralist. -***************************************************************************************/ - char *c = definition; - paralist *first = 0, *next = 0; - - while (*c) { - /* Find start of next substring */ - while (isspace (*c)) - c++; - - /* Keep a handle to the start of the list, so we have something to return */ - if (0==first) - first = next = pj_mkparam_ws (c); - else - next = next->next = pj_mkparam_ws (c); - if (0==next) - return pj_dealloc_params (ctx, first, ENOMEM); - - /* And skip to the end of the substring */ - while ((!isspace(*c)) && 0!=*c) - c++; - } - - if( next == 0 ) - return 0; - - /* Terminate list and return */ - next->next = 0; - return first; -} - - - -/**************************************************************************************/ -static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { -/*************************************************************************************** - Read a section of an init file. Return its contents as a plain character string. - It is the duty of the caller to free the memory allocated for the string. -***************************************************************************************/ -#define MAX_LINE_LENGTH 1000 - size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1); - char *fname, *section; - const char *key; - char *buffer = 0; - char *line = 0; - PAFile fid; - size_t n; - - - line = pj_malloc (MAX_LINE_LENGTH + 1); - if (0==line) - return 0; - - fname = pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3); - if (0==fname) { - pj_dealloc (line); - return 0; - } - - /* Support "init=file:section", "+init=file:section", and "file:section" format */ - key = strstr (name, "init="); - if (0==key) - key = name; - else - key += 5; - if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) { - pj_dealloc (fname); - pj_dealloc (line); - return 0; - } - memmove (fname, key, strlen (key) + 1); - - /* Locate the name of the section we search for */ - section = strrchr(fname, ':'); - if (0==section) { - proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING); - pj_dealloc (fname); - pj_dealloc (line); - return 0; - } - *section = 0; - section++; - n = strlen (section); - pj_log (ctx, PJ_LOG_TRACE, - "get_init_string: searching for section [%s] in init file [%s]", - section, fname); - - fid = pj_open_lib (ctx, fname, "rt"); - if (0==fid) { - pj_dealloc (fname); - pj_dealloc (line); - proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); - return 0; - } - - /* Search for section in init file */ - for (;;) { - - /* End of file? */ - if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) { - pj_dealloc (buffer); - pj_dealloc (fname); - pj_dealloc (line); - pj_ctx_fclose (ctx, fid); - proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); - return 0; - } - - /* At start of right section? */ - pj_chomp (line); - if ('<'!=line[0]) - continue; - if (strlen (line) < n + 2) - continue; - if (line[n + 1] != '>') - continue; - if (0==strncmp (line + 1, section, n)) - break; - } - - /* We're at the first line of the right section - copy line to buffer */ - buffer = pj_malloc (current_buffer_size); - if (0==buffer) { - pj_dealloc (fname); - pj_dealloc (line); - pj_ctx_fclose (ctx, fid); - return 0; - } - - /* Skip the "
" indicator, and copy the rest of the line over */ - strcpy (buffer, line + strlen (section) + 2); - - /* Copy the remaining lines of the section to buffer */ - for (;;) { - char *end_i_cator; - size_t next_length, buffer_length; - - /* Did the section end somewhere in the most recently read line? */ - end_i_cator = strchr (buffer, '<'); - if (end_i_cator) { - *end_i_cator = 0; - break; - } - - /* End of file? - done! */ - if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) - break; - - /* Otherwise, handle the line. It MAY be the start of the next section, */ - /* but that will be handled at the start of next trip through the loop */ - buffer_length = strlen (buffer); - pj_chomp (line); /* Remove '#' style comments */ - next_length = strlen (line) + buffer_length + 2; - if (next_length > current_buffer_size) { - char *b = pj_malloc (2 * current_buffer_size); - if (0==b) { - pj_dealloc (buffer); - buffer = 0; - break; - } - strcpy (b, buffer); - current_buffer_size *= 2; - pj_dealloc (buffer); - buffer = b; - } - buffer[buffer_length] = ' '; - strcpy (buffer + buffer_length + 1, line); - } - - pj_ctx_fclose (ctx, fid); - pj_dealloc (fname); - pj_dealloc (line); - if (0==buffer) - return 0; - pj_shrink (buffer); - pj_log (ctx, PJ_LOG_TRACE, "key=%s, value: [%s]", key, buffer); - return buffer; -} - - - -/************************************************************************/ -static paralist *get_init(PJ_CONTEXT *ctx, const char *key, int allow_init_epsg) { -/************************************************************************* -Expand key from buffer or (if not in buffer) from init file -*************************************************************************/ - const char *xkey; - char *definition = 0; - paralist *init_items = 0; - - /* support "init=file:section", "+init=file:section", and "file:section" format */ - xkey = strstr (key, "init="); - if (0==xkey) - xkey = key; - else - xkey += 5; - pj_log (ctx, PJ_LOG_TRACE, "get_init: searching cache for key: [%s]", xkey); - - /* Is file/key pair already in cache? */ - init_items = pj_search_initcache (xkey); - if (init_items) - return init_items; - - if( (strncmp(xkey, "epsg:", 5) == 0 || strncmp(xkey, "IGNF:", 5) == 0) ) { - char unused[256]; - char initname[5]; - int exists; - - memcpy(initname, xkey, 4); - initname[4] = 0; - - if( strncmp(xkey, "epsg:", 5) == 0 ) { - exists = ctx->epsg_file_exists; - if( exists < 0 ) { - exists = pj_find_file(ctx, initname, unused, sizeof(unused)); - ctx->epsg_file_exists = exists; - } - } else { - exists = pj_find_file(ctx, initname, unused, sizeof(unused)); - } - - if( !exists ) { - const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", NULL }; - char szInitStr[7 + 64]; - PJ_OBJ* src; - const char* proj_string; - - pj_ctx_set_errno( ctx, 0 ); - - if( !allow_init_epsg ) { - pj_log (ctx, PJ_LOG_TRACE, "%s expansion disallowed", xkey); - return 0; - } - if( strlen(xkey) > 64 ) { - return 0; - } - strcpy(szInitStr, "+init="); - strcat(szInitStr, xkey); - - src = proj_obj_create_from_user_input(ctx, szInitStr, optionsProj4Mode); - if( !src ) { - return 0; - } - - proj_string = proj_obj_as_proj_string(ctx, src, PJ_PROJ_4, NULL); - if( !proj_string ) { - proj_obj_destroy(src); - return 0; - } - definition = (char*)calloc(1, strlen(proj_string)+1); - if( definition ) { - strcpy(definition, proj_string); - } - - proj_obj_destroy(src); - } - } - - if( !definition ) { - /* If not, we must read it from file */ - pj_log (ctx, PJ_LOG_TRACE, - "get_init: searching on in init files for [%s]", xkey); - definition = get_init_string (ctx, xkey); - } - - if (0==definition) - return 0; - init_items = string_to_paralist (ctx, definition); - if (init_items) - pj_log (ctx, PJ_LOG_TRACE, "get_init: got [%s], paralist[0,1]: [%s,%s]", - definition, - init_items->param, - init_items->next ? init_items->next->param : "(empty)"); - pj_dealloc (definition); - if (0==init_items) - return 0; - - /* We found it in file - now insert into the cache, before returning */ - pj_insert_initcache (xkey, init_items); - return init_items; -} - - - -static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, const char *key, int allow_init_epsg) { - paralist *defaults, *last = 0; - char keystring[ID_TAG_MAX + 20]; - paralist *next, *proj; - int err; - - if (0==start) - return 0; - - if (strlen(key) > ID_TAG_MAX) - return 0; - - /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */ - /* or implicitly, because we are initializing a pipeline) */ - if (pj_param_exists (start, "no_defs")) - return start; - proj = pj_param_exists (start, "proj"); - if (0==proj) - return start; - if (strlen (proj->param) < 6) - return start; - if (0==strcmp ("pipeline", proj->param + 5)) - return start; - - err = pj_ctx_get_errno (ctx); - pj_ctx_set_errno (ctx, 0); - - /* Locate end of start-list */ - for (last = start; last->next; last = last->next); - - strcpy (keystring, "proj_def.dat:"); - strcat (keystring, key); - defaults = get_init (ctx, keystring, allow_init_epsg); - - /* Defaults are optional - so we don't care if we cannot open the file */ - pj_ctx_set_errno (ctx, err); - - if (!defaults) - return last; - - /* Loop over all default items */ - for (next = defaults; next; next = next->next) { - - /* Don't override existing parameter value of same name */ - if (pj_param_exists (start, next->param)) - continue; - - /* Don't default ellipse if datum, ellps or any ellipsoid information is set */ - if (0==strncmp(next->param,"ellps=", 6)) { - if (pj_param_exists (start, "datum")) continue; - if (pj_param_exists (start, "ellps")) continue; - if (pj_param_exists (start, "a")) continue; - if (pj_param_exists (start, "b")) continue; - if (pj_param_exists (start, "rf")) continue; - if (pj_param_exists (start, "f")) continue; - if (pj_param_exists (start, "e")) continue; - if (pj_param_exists (start, "es")) continue; - } - - /* If we're here, it's OK to append the current default item */ - last = last->next = pj_mkparam(next->param); - } - last->next = 0; - - pj_dealloc_params (ctx, defaults, 0); - return last; -} - -/*****************************************************************************/ -static paralist *pj_expand_init_internal(PJ_CONTEXT *ctx, paralist *init, int allow_init_epsg) { -/****************************************************************************** -Append expansion of to the paralist . The expansion is appended, -rather than inserted at 's place, since may contain -overrides to the expansion. These must take precedence, and hence come first -in the expanded list. - -Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm -zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid. - -The expression 'init=foo:bar ellps=intl' will then expand to: - - 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80', - -where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, -turning the expansion into an UTM projection on the Hayford ellipsoid. - -Note that 'init=foo:bar' stays in the list. It is ignored after expansion. - -******************************************************************************/ - paralist *last; - paralist *expn; - - /* Nowhere to start? */ - if (0==init) - return 0; - - expn = get_init(ctx, init->param, allow_init_epsg); - - /* Nothing in expansion? */ - if (0==expn) - return 0; - - /* Locate the end of the list */ - for (last = init; last && last->next; last = last->next); - - /* Then append and return */ - last->next = expn; - return init; -} - -paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) { - return pj_expand_init_internal(ctx, init, TRUE); -} - - -/************************************************************************/ -/* pj_init_plus() */ -/* */ -/* Same as pj_init() except it takes one argument string with */ -/* individual arguments preceded by '+', such as "+proj=utm */ -/* +zone=11 +ellps=WGS84". */ -/************************************************************************/ - -PJ * -pj_init_plus( const char *definition ) - -{ - return pj_init_plus_ctx( pj_get_default_ctx(), definition ); -} - -PJ * -pj_init_plus_ctx( projCtx ctx, const char *definition ) -{ -#define MAX_ARG 200 - char *argv[MAX_ARG]; - char *defn_copy; - int argc = 0, i, blank_count = 0; - PJ *result = NULL; - - /* make a copy that we can manipulate */ - defn_copy = (char *) pj_malloc( strlen(definition)+1 ); - if (!defn_copy) - return NULL; - strcpy( defn_copy, definition ); - - /* split into arguments based on '+' and trim white space */ - - for( i = 0; defn_copy[i] != '\0'; i++ ) - { - switch( defn_copy[i] ) - { - case '+': - if( i == 0 || defn_copy[i-1] == '\0' || blank_count > 0 ) - { - /* trim trailing spaces from the previous param */ - if( blank_count > 0 ) - { - defn_copy[i - blank_count] = '\0'; - blank_count = 0; - } - - if( argc+1 == MAX_ARG ) - { - pj_dalloc( defn_copy ); - pj_ctx_set_errno( ctx, PJD_ERR_UNPARSEABLE_CS_DEF ); - return 0; - } - - argv[argc++] = defn_copy + i + 1; - } - break; - - case ' ': - case '\t': - case '\n': - /* trim leading spaces from the current param */ - if( i == 0 || defn_copy[i-1] == '\0' || argc == 0 || argv[argc-1] == defn_copy + i ) - defn_copy[i] = '\0'; - else - blank_count++; - break; - - default: - /* reset blank_count */ - blank_count = 0; - } - } - /* trim trailing spaces from the last param */ - defn_copy[i - blank_count] = '\0'; - - /* perform actual initialization */ - result = pj_init_ctx( ctx, argc, argv ); - - pj_dalloc( defn_copy ); - return result; -} - - - -/************************************************************************/ -/* pj_init() */ -/* */ -/* Main entry point for initialing a PJ projections */ -/* definition. Note that the projection specific function is */ -/* called to do the initial allocation so it can be created */ -/* large enough to hold projection specific parameters. */ -/************************************************************************/ - -PJ * -pj_init(int argc, char **argv) { - return pj_init_ctx( pj_get_default_ctx(), argc, argv ); -} - - -static PJ_CONSTRUCTOR locate_constructor (const char *name) { - int i; - const char *s; - const PJ_OPERATIONS *operations; - operations = proj_list_operations(); - for (i = 0; (s = operations[i].id) && strcmp(name, s) ; ++i) ; - if (0==s) - return 0; - return (PJ_CONSTRUCTOR) operations[i].proj; -} - - -PJ * -pj_init_ctx(projCtx ctx, int argc, char **argv) { - /* Legacy interface: allow init=epsg:XXXX syntax by default */ - int allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, TRUE); - return pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg); -} - - -PJ * -pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_init_epsg) { - const char *s; - char *name; - PJ_CONSTRUCTOR proj; - paralist *curr, *init, *start; - int i; - int err; - PJ *PIN = 0; - int n_pipelines = 0; - int n_inits = 0; - const PJ_UNITS *units; - const PJ_PRIME_MERIDIANS *prime_meridians; - - if (0==ctx) - ctx = pj_get_default_ctx (); - - ctx->last_errno = 0; - - if (argc <= 0) { - pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); - return 0; - } - - /* count occurrences of pipelines and inits */ - for (i = 0; i < argc; ++i) { - if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") ) - n_pipelines++; - if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5)) - n_inits++; - } - - /* can't have nested pipelines directly */ - if (n_pipelines > 1) { - pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); - return 0; - } - - /* don't allow more than one +init in non-pipeline operations */ - if (n_pipelines == 0 && n_inits > 1) { - pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS); - return 0; - } - - - /* put arguments into internal linked list */ - start = curr = pj_mkparam(argv[0]); - if (!curr) - return pj_dealloc_params (ctx, start, ENOMEM); - - for (i = 1; i < argc; ++i) { - curr->next = pj_mkparam(argv[i]); - if (!curr->next) - return pj_dealloc_params (ctx, start, ENOMEM); - curr = curr->next; - } - - - /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */ - /* expanded in the individual pipeline steps during pipeline initialization. */ - /* Potentially this leads to many nested pipelines, which shouldn't be a */ - /* problem when '+init's are expanded as late as possible. */ - init = pj_param_exists (start, "init"); - if (init && n_pipelines == 0) { - init = pj_expand_init_internal (ctx, init, allow_init_epsg); - if (!init) - return pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS); - } - if (ctx->last_errno) - return pj_dealloc_params (ctx, start, ctx->last_errno); - - /* Find projection selection */ - curr = pj_param_exists (start, "proj"); - if (0==curr) - return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); - name = curr->param; - if (strlen (name) < 6) - return pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); - name += 5; - - proj = locate_constructor (name); - if (0==proj) - return pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); - - - /* Append general and projection specific defaults to the definition list */ - append_defaults_to_paralist (ctx, start, "general", allow_init_epsg); - append_defaults_to_paralist (ctx, start, name, allow_init_epsg); - - - /* Allocate projection structure */ - PIN = proj(0); - if (0==PIN) - return pj_dealloc_params (ctx, start, ENOMEM); - - - PIN->ctx = ctx; - PIN->params = start; - PIN->is_latlong = 0; - PIN->is_geocent = 0; - PIN->is_long_wrap_set = 0; - PIN->long_wrap_center = 0.0; - strcpy( PIN->axis, "enu" ); - - PIN->gridlist = NULL; - PIN->gridlist_count = 0; - - PIN->vgridlist_geoid = NULL; - PIN->vgridlist_geoid_count = 0; - - /* Set datum parameters. Similarly to +init parameters we want to expand */ - /* +datum parameters as late as possible when dealing with pipelines. */ - /* otherwise only the first occurrence of +datum will be expanded and that */ - if (n_pipelines == 0) { - if (pj_datum_set(ctx, start, PIN)) - return pj_default_destructor (PIN, proj_errno(PIN)); - } - - err = pj_ellipsoid (PIN); - - if (err) { - /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */ - if (PIN->need_ellps) { - pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); - return pj_default_destructor (PIN, proj_errno(PIN)); - } - else { - if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN)) - proj_errno_reset (PIN); - PIN->f = 1.0/298.257223563; - PIN->a_orig = PIN->a = 6378137.0; - PIN->es_orig = PIN->es = PIN->f*(2-PIN->f); - } - } - PIN->a_orig = PIN->a; - PIN->es_orig = PIN->es; - if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) - return pj_default_destructor (PIN, PJD_ERR_ECCENTRICITY_IS_ONE); - - /* Now that we have ellipse information check for WGS84 datum */ - if( PIN->datum_type == PJD_3PARAM - && PIN->datum_params[0] == 0.0 - && PIN->datum_params[1] == 0.0 - && PIN->datum_params[2] == 0.0 - && PIN->a == 6378137.0 - && ABS(PIN->es - 0.006694379990) < 0.000000000050 )/*WGS84/GRS80*/ - { - PIN->datum_type = PJD_WGS84; - } - - /* Set PIN->geoc coordinate system */ - PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i); - - /* Over-ranging flag */ - PIN->over = pj_param(ctx, start, "bover").i; - - /* Vertical datum geoid grids */ - PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i; - if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */ - pj_param(ctx, start, "sgeoidgrids"); - - /* Longitude center for wrapping */ - PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i; - if (PIN->is_long_wrap_set) { - PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f; - /* Don't accept excessive values otherwise we might perform badly */ - /* when correcting longitudes around it */ - /* The test is written this way to error on long_wrap_center "=" NaN */ - if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) ) - return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); - } - - /* Axis orientation */ - if( (pj_param(ctx, start,"saxis").s) != NULL ) - { - const char *axis_legal = "ewnsud"; - const char *axis_arg = pj_param(ctx, start,"saxis").s; - if( strlen(axis_arg) != 3 ) - return pj_default_destructor (PIN, PJD_ERR_AXIS); - - if( strchr( axis_legal, axis_arg[0] ) == NULL - || strchr( axis_legal, axis_arg[1] ) == NULL - || strchr( axis_legal, axis_arg[2] ) == NULL) - return pj_default_destructor (PIN, PJD_ERR_AXIS); - - /* TODO: it would be nice to validate we don't have on axis repeated */ - strcpy( PIN->axis, axis_arg ); - } - - /* Central meridian */ - PIN->lam0=pj_param(ctx, start, "rlon_0").f; - - /* Central latitude */ - PIN->phi0 = pj_param(ctx, start, "rlat_0").f; - - /* False easting and northing */ - PIN->x0 = pj_param(ctx, start, "dx_0").f; - PIN->y0 = pj_param(ctx, start, "dy_0").f; - PIN->z0 = pj_param(ctx, start, "dz_0").f; - PIN->t0 = pj_param(ctx, start, "dt_0").f; - - /* General scaling factor */ - if (pj_param(ctx, start, "tk_0").i) - PIN->k0 = pj_param(ctx, start, "dk_0").f; - else if (pj_param(ctx, start, "tk").i) - PIN->k0 = pj_param(ctx, start, "dk").f; - else - PIN->k0 = 1.; - if (PIN->k0 <= 0.) - return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); - - /* Set units */ - units = proj_list_units(); - s = 0; - if ((name = pj_param(ctx, start, "sunits").s) != NULL) { - for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; - if (!s) - return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); - s = units[i].to_meter; - } - if (s || (s = pj_param(ctx, start, "sto_meter").s)) { - double factor; - int ratio = 0; - - /* ratio number? */ - if (strlen (s) > 1 && s[0] == '1' && s[1]=='/') { - ratio = 1; - s += 2; - } - - factor = pj_strtod(s, 0); - if ((factor <= 0.0) || (1/factor==0)) - return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); - - PIN->to_meter = ratio? 1 / factor: factor; - PIN->fr_meter = 1 / PIN->to_meter; - - } else - PIN->to_meter = PIN->fr_meter = 1.; - - /* Set vertical units */ - s = 0; - if ((name = pj_param(ctx, start, "svunits").s) != NULL) { - for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; - if (!s) - return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); - s = units[i].to_meter; - } - if (s || (s = pj_param(ctx, start, "svto_meter").s)) { - PIN->vto_meter = pj_strtod(s, 0); - if (*s == '/') /* ratio number */ - PIN->vto_meter /= pj_strtod(++s, 0); - if (PIN->vto_meter <= 0.0) - return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); - PIN->vfr_meter = 1. / PIN->vto_meter; - } else { - PIN->vto_meter = PIN->to_meter; - PIN->vfr_meter = PIN->fr_meter; - } - - /* Prime meridian */ - prime_meridians = proj_list_prime_meridians(); - s = 0; - if ((name = pj_param(ctx, start, "spm").s) != NULL) { - const char *value = NULL; - char *next_str = NULL; - - for (i = 0; prime_meridians[i].id != NULL; ++i ) - { - if( strcmp(name,prime_meridians[i].id) == 0 ) - { - value = prime_meridians[i].defn; - break; - } - } - - if( value == NULL - && (dmstor_ctx(ctx,name,&next_str) != 0.0 || *name == '0') - && *next_str == '\0' ) - value = name; - - if (!value) - return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN); - PIN->from_greenwich = dmstor_ctx(ctx,value,NULL); - } - else - PIN->from_greenwich = 0.0; - - /* Private object for the geodesic functions */ - PIN->geod = pj_calloc (1, sizeof (struct geod_geodesic)); - if (0==PIN->geod) - return pj_default_destructor (PIN, ENOMEM); - geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es))); - - /* Projection specific initialization */ - err = proj_errno_reset (PIN); - PIN = proj(PIN); - if (proj_errno (PIN)) { - pj_free(PIN); - return 0; - } - proj_errno_restore (PIN, err); - return PIN; -} diff --git a/src/pj_init.cpp b/src/pj_init.cpp new file mode 100644 index 00000000..b09d70ed --- /dev/null +++ b/src/pj_init.cpp @@ -0,0 +1,888 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Initialize projection object from string definition. Includes + * pj_init(), pj_init_plus() and pj_free() function. + * Author: Gerald Evenden, Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * Copyright (c) 2002, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include +#include +#include + +#include "geodesic.h" +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + + +/**************************************************************************************/ +static paralist *string_to_paralist (PJ_CONTEXT *ctx, char *definition) { +/*************************************************************************************** + Convert a string (presumably originating from get_init_string) to a paralist. +***************************************************************************************/ + char *c = definition; + paralist *first = 0, *next = 0; + + while (*c) { + /* Find start of next substring */ + while (isspace (*c)) + c++; + + /* Keep a handle to the start of the list, so we have something to return */ + if (0==first) + first = next = pj_mkparam_ws (c); + else + next = next->next = pj_mkparam_ws (c); + if (0==next) { + pj_dealloc_params (ctx, first, ENOMEM); + return nullptr; + } + + /* And skip to the end of the substring */ + while ((!isspace(*c)) && 0!=*c) + c++; + } + + if( next == 0 ) + return 0; + + /* Terminate list and return */ + next->next = 0; + return first; +} + + + +/**************************************************************************************/ +static char *get_init_string (PJ_CONTEXT *ctx, const char *name) { +/*************************************************************************************** + Read a section of an init file. Return its contents as a plain character string. + It is the duty of the caller to free the memory allocated for the string. +***************************************************************************************/ +#define MAX_LINE_LENGTH 1000 + size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1); + char *fname, *section; + const char *key; + char *buffer = 0; + char *line = 0; + PAFile fid; + size_t n; + + + line = static_cast(pj_malloc (MAX_LINE_LENGTH + 1)); + if (0==line) + return 0; + + fname = static_cast(pj_malloc (MAX_PATH_FILENAME+ID_TAG_MAX+3)); + if (0==fname) { + pj_dealloc (line); + return 0; + } + + /* Support "init=file:section", "+init=file:section", and "file:section" format */ + key = strstr (name, "init="); + if (0==key) + key = name; + else + key += 5; + if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen (key)) { + pj_dealloc (fname); + pj_dealloc (line); + return 0; + } + memmove (fname, key, strlen (key) + 1); + + /* Locate the name of the section we search for */ + section = strrchr(fname, ':'); + if (0==section) { + proj_context_errno_set (ctx, PJD_ERR_NO_COLON_IN_INIT_STRING); + pj_dealloc (fname); + pj_dealloc (line); + return 0; + } + *section = 0; + section++; + n = strlen (section); + pj_log (ctx, PJ_LOG_TRACE, + "get_init_string: searching for section [%s] in init file [%s]", + section, fname); + + fid = pj_open_lib (ctx, fname, "rt"); + if (0==fid) { + pj_dealloc (fname); + pj_dealloc (line); + proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); + return 0; + } + + /* Search for section in init file */ + for (;;) { + + /* End of file? */ + if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) { + pj_dealloc (buffer); + pj_dealloc (fname); + pj_dealloc (line); + pj_ctx_fclose (ctx, fid); + proj_context_errno_set (ctx, PJD_ERR_NO_OPTION_IN_INIT_FILE); + return 0; + } + + /* At start of right section? */ + pj_chomp (line); + if ('<'!=line[0]) + continue; + if (strlen (line) < n + 2) + continue; + if (line[n + 1] != '>') + continue; + if (0==strncmp (line + 1, section, n)) + break; + } + + /* We're at the first line of the right section - copy line to buffer */ + buffer = static_cast(pj_malloc (current_buffer_size)); + if (0==buffer) { + pj_dealloc (fname); + pj_dealloc (line); + pj_ctx_fclose (ctx, fid); + return 0; + } + + /* Skip the "
" indicator, and copy the rest of the line over */ + strcpy (buffer, line + strlen (section) + 2); + + /* Copy the remaining lines of the section to buffer */ + for (;;) { + char *end_i_cator; + size_t next_length, buffer_length; + + /* Did the section end somewhere in the most recently read line? */ + end_i_cator = strchr (buffer, '<'); + if (end_i_cator) { + *end_i_cator = 0; + break; + } + + /* End of file? - done! */ + if (0==pj_ctx_fgets (ctx, line, MAX_LINE_LENGTH, fid)) + break; + + /* Otherwise, handle the line. It MAY be the start of the next section, */ + /* but that will be handled at the start of next trip through the loop */ + buffer_length = strlen (buffer); + pj_chomp (line); /* Remove '#' style comments */ + next_length = strlen (line) + buffer_length + 2; + if (next_length > current_buffer_size) { + char *b = static_cast(pj_malloc (2 * current_buffer_size)); + if (0==b) { + pj_dealloc (buffer); + buffer = 0; + break; + } + strcpy (b, buffer); + current_buffer_size *= 2; + pj_dealloc (buffer); + buffer = b; + } + buffer[buffer_length] = ' '; + strcpy (buffer + buffer_length + 1, line); + } + + pj_ctx_fclose (ctx, fid); + pj_dealloc (fname); + pj_dealloc (line); + if (0==buffer) + return 0; + pj_shrink (buffer); + pj_log (ctx, PJ_LOG_TRACE, "key=%s, value: [%s]", key, buffer); + return buffer; +} + + + +/************************************************************************/ +static paralist *get_init(PJ_CONTEXT *ctx, const char *key, int allow_init_epsg) { +/************************************************************************* +Expand key from buffer or (if not in buffer) from init file +*************************************************************************/ + const char *xkey; + char *definition = 0; + paralist *init_items = 0; + + /* support "init=file:section", "+init=file:section", and "file:section" format */ + xkey = strstr (key, "init="); + if (0==xkey) + xkey = key; + else + xkey += 5; + pj_log (ctx, PJ_LOG_TRACE, "get_init: searching cache for key: [%s]", xkey); + + /* Is file/key pair already in cache? */ + init_items = pj_search_initcache (xkey); + if (init_items) + return init_items; + + if( (strncmp(xkey, "epsg:", 5) == 0 || strncmp(xkey, "IGNF:", 5) == 0) ) { + char unused[256]; + char initname[5]; + int exists; + + memcpy(initname, xkey, 4); + initname[4] = 0; + + if( strncmp(xkey, "epsg:", 5) == 0 ) { + exists = ctx->epsg_file_exists; + if( exists < 0 ) { + exists = pj_find_file(ctx, initname, unused, sizeof(unused)); + ctx->epsg_file_exists = exists; + } + } else { + exists = pj_find_file(ctx, initname, unused, sizeof(unused)); + } + + if( !exists ) { + const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", NULL }; + char szInitStr[7 + 64]; + PJ_OBJ* src; + const char* proj_string; + + pj_ctx_set_errno( ctx, 0 ); + + if( !allow_init_epsg ) { + pj_log (ctx, PJ_LOG_TRACE, "%s expansion disallowed", xkey); + return 0; + } + if( strlen(xkey) > 64 ) { + return 0; + } + strcpy(szInitStr, "+init="); + strcat(szInitStr, xkey); + + src = proj_obj_create_from_user_input(ctx, szInitStr, optionsProj4Mode); + if( !src ) { + return 0; + } + + proj_string = proj_obj_as_proj_string(ctx, src, PJ_PROJ_4, NULL); + if( !proj_string ) { + proj_obj_destroy(src); + return 0; + } + definition = (char*)calloc(1, strlen(proj_string)+1); + if( definition ) { + strcpy(definition, proj_string); + } + + proj_obj_destroy(src); + } + } + + if( !definition ) { + /* If not, we must read it from file */ + pj_log (ctx, PJ_LOG_TRACE, + "get_init: searching on in init files for [%s]", xkey); + definition = get_init_string (ctx, xkey); + } + + if (0==definition) + return 0; + init_items = string_to_paralist (ctx, definition); + if (init_items) + pj_log (ctx, PJ_LOG_TRACE, "get_init: got [%s], paralist[0,1]: [%s,%s]", + definition, + init_items->param, + init_items->next ? init_items->next->param : "(empty)"); + pj_dealloc (definition); + if (0==init_items) + return 0; + + /* We found it in file - now insert into the cache, before returning */ + pj_insert_initcache (xkey, init_items); + return init_items; +} + + + +static paralist *append_defaults_to_paralist (PJ_CONTEXT *ctx, paralist *start, const char *key, int allow_init_epsg) { + paralist *defaults, *last = 0; + char keystring[ID_TAG_MAX + 20]; + paralist *next, *proj; + int err; + + if (0==start) + return 0; + + if (strlen(key) > ID_TAG_MAX) + return 0; + + /* Set defaults, unless inhibited (either explicitly through a "no_defs" token */ + /* or implicitly, because we are initializing a pipeline) */ + if (pj_param_exists (start, "no_defs")) + return start; + proj = pj_param_exists (start, "proj"); + if (0==proj) + return start; + if (strlen (proj->param) < 6) + return start; + if (0==strcmp ("pipeline", proj->param + 5)) + return start; + + err = pj_ctx_get_errno (ctx); + pj_ctx_set_errno (ctx, 0); + + /* Locate end of start-list */ + for (last = start; last->next; last = last->next); + + strcpy (keystring, "proj_def.dat:"); + strcat (keystring, key); + defaults = get_init (ctx, keystring, allow_init_epsg); + + /* Defaults are optional - so we don't care if we cannot open the file */ + pj_ctx_set_errno (ctx, err); + + if (!defaults) + return last; + + /* Loop over all default items */ + for (next = defaults; next; next = next->next) { + + /* Don't override existing parameter value of same name */ + if (pj_param_exists (start, next->param)) + continue; + + /* Don't default ellipse if datum, ellps or any ellipsoid information is set */ + if (0==strncmp(next->param,"ellps=", 6)) { + if (pj_param_exists (start, "datum")) continue; + if (pj_param_exists (start, "ellps")) continue; + if (pj_param_exists (start, "a")) continue; + if (pj_param_exists (start, "b")) continue; + if (pj_param_exists (start, "rf")) continue; + if (pj_param_exists (start, "f")) continue; + if (pj_param_exists (start, "e")) continue; + if (pj_param_exists (start, "es")) continue; + } + + /* If we're here, it's OK to append the current default item */ + last = last->next = pj_mkparam(next->param); + } + last->next = 0; + + pj_dealloc_params (ctx, defaults, 0); + return last; +} + +/*****************************************************************************/ +static paralist *pj_expand_init_internal(PJ_CONTEXT *ctx, paralist *init, int allow_init_epsg) { +/****************************************************************************** +Append expansion of to the paralist . The expansion is appended, +rather than inserted at 's place, since may contain +overrides to the expansion. These must take precedence, and hence come first +in the expanded list. + +Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm +zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid. + +The expression 'init=foo:bar ellps=intl' will then expand to: + + 'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80', + +where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence, +turning the expansion into an UTM projection on the Hayford ellipsoid. + +Note that 'init=foo:bar' stays in the list. It is ignored after expansion. + +******************************************************************************/ + paralist *last; + paralist *expn; + + /* Nowhere to start? */ + if (0==init) + return 0; + + expn = get_init(ctx, init->param, allow_init_epsg); + + /* Nothing in expansion? */ + if (0==expn) + return 0; + + /* Locate the end of the list */ + for (last = init; last && last->next; last = last->next); + + /* Then append and return */ + last->next = expn; + return init; +} + +paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) { + return pj_expand_init_internal(ctx, init, TRUE); +} + + +/************************************************************************/ +/* pj_init_plus() */ +/* */ +/* Same as pj_init() except it takes one argument string with */ +/* individual arguments preceded by '+', such as "+proj=utm */ +/* +zone=11 +ellps=WGS84". */ +/************************************************************************/ + +PJ * +pj_init_plus( const char *definition ) + +{ + return pj_init_plus_ctx( pj_get_default_ctx(), definition ); +} + +PJ * +pj_init_plus_ctx( projCtx ctx, const char *definition ) +{ +#define MAX_ARG 200 + char *argv[MAX_ARG]; + char *defn_copy; + int argc = 0, i, blank_count = 0; + PJ *result = NULL; + + /* make a copy that we can manipulate */ + defn_copy = (char *) pj_malloc( strlen(definition)+1 ); + if (!defn_copy) + return NULL; + strcpy( defn_copy, definition ); + + /* split into arguments based on '+' and trim white space */ + + for( i = 0; defn_copy[i] != '\0'; i++ ) + { + switch( defn_copy[i] ) + { + case '+': + if( i == 0 || defn_copy[i-1] == '\0' || blank_count > 0 ) + { + /* trim trailing spaces from the previous param */ + if( blank_count > 0 ) + { + defn_copy[i - blank_count] = '\0'; + blank_count = 0; + } + + if( argc+1 == MAX_ARG ) + { + pj_dalloc( defn_copy ); + pj_ctx_set_errno( ctx, PJD_ERR_UNPARSEABLE_CS_DEF ); + return 0; + } + + argv[argc++] = defn_copy + i + 1; + } + break; + + case ' ': + case '\t': + case '\n': + /* trim leading spaces from the current param */ + if( i == 0 || defn_copy[i-1] == '\0' || argc == 0 || argv[argc-1] == defn_copy + i ) + defn_copy[i] = '\0'; + else + blank_count++; + break; + + default: + /* reset blank_count */ + blank_count = 0; + } + } + /* trim trailing spaces from the last param */ + defn_copy[i - blank_count] = '\0'; + + /* perform actual initialization */ + result = pj_init_ctx( ctx, argc, argv ); + + pj_dalloc( defn_copy ); + return result; +} + + + +/************************************************************************/ +/* pj_init() */ +/* */ +/* Main entry point for initialing a PJ projections */ +/* definition. Note that the projection specific function is */ +/* called to do the initial allocation so it can be created */ +/* large enough to hold projection specific parameters. */ +/************************************************************************/ + +PJ * +pj_init(int argc, char **argv) { + return pj_init_ctx( pj_get_default_ctx(), argc, argv ); +} + + +static PJ_CONSTRUCTOR locate_constructor (const char *name) { + int i; + const char *s; + const PJ_OPERATIONS *operations; + operations = proj_list_operations(); + for (i = 0; (s = operations[i].id) && strcmp(name, s) ; ++i) ; + if (0==s) + return 0; + return (PJ_CONSTRUCTOR) operations[i].proj; +} + + +PJ * +pj_init_ctx(projCtx ctx, int argc, char **argv) { + /* Legacy interface: allow init=epsg:XXXX syntax by default */ + int allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, TRUE); + return pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg); +} + + +PJ * +pj_init_ctx_with_allow_init_epsg(projCtx ctx, int argc, char **argv, int allow_init_epsg) { + const char *s; + char *name; + PJ_CONSTRUCTOR proj; + paralist *curr, *init, *start; + int i; + int err; + PJ *PIN = 0; + int n_pipelines = 0; + int n_inits = 0; + const PJ_UNITS *units; + const PJ_PRIME_MERIDIANS *prime_meridians; + + if (0==ctx) + ctx = pj_get_default_ctx (); + + ctx->last_errno = 0; + + if (argc <= 0) { + pj_ctx_set_errno (ctx, PJD_ERR_NO_ARGS); + return 0; + } + + /* count occurrences of pipelines and inits */ + for (i = 0; i < argc; ++i) { + if (!strcmp (argv[i], "+proj=pipeline") || !strcmp(argv[i], "proj=pipeline") ) + n_pipelines++; + if (!strncmp (argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5)) + n_inits++; + } + + /* can't have nested pipelines directly */ + if (n_pipelines > 1) { + pj_ctx_set_errno (ctx, PJD_ERR_MALFORMED_PIPELINE); + return 0; + } + + /* don't allow more than one +init in non-pipeline operations */ + if (n_pipelines == 0 && n_inits > 1) { + pj_ctx_set_errno (ctx, PJD_ERR_TOO_MANY_INITS); + return 0; + } + + + /* put arguments into internal linked list */ + start = curr = pj_mkparam(argv[0]); + if (!curr) { + pj_dealloc_params (ctx, start, ENOMEM); + return nullptr; + } + + for (i = 1; i < argc; ++i) { + curr->next = pj_mkparam(argv[i]); + if (!curr->next) { + pj_dealloc_params (ctx, start, ENOMEM); + return nullptr; + } + curr = curr->next; + } + + + /* Only expand '+init's in non-pipeline operations. '+init's in pipelines are */ + /* expanded in the individual pipeline steps during pipeline initialization. */ + /* Potentially this leads to many nested pipelines, which shouldn't be a */ + /* problem when '+init's are expanded as late as possible. */ + init = pj_param_exists (start, "init"); + if (init && n_pipelines == 0) { + init = pj_expand_init_internal (ctx, init, allow_init_epsg); + if (!init) { + pj_dealloc_params (ctx, start, PJD_ERR_NO_ARGS); + return nullptr; + } + } + if (ctx->last_errno) { + pj_dealloc_params (ctx, start, ctx->last_errno); + return nullptr; + } + + /* Find projection selection */ + curr = pj_param_exists (start, "proj"); + if (0==curr) { + pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); + return nullptr; + } + name = curr->param; + if (strlen (name) < 6) { + pj_dealloc_params (ctx, start, PJD_ERR_PROJ_NOT_NAMED); + return nullptr; + } + name += 5; + + proj = locate_constructor (name); + if (0==proj) { + pj_dealloc_params (ctx, start, PJD_ERR_UNKNOWN_PROJECTION_ID); + return nullptr; + } + + + /* Append general and projection specific defaults to the definition list */ + append_defaults_to_paralist (ctx, start, "general", allow_init_epsg); + append_defaults_to_paralist (ctx, start, name, allow_init_epsg); + + + /* Allocate projection structure */ + PIN = proj(0); + if (0==PIN) { + pj_dealloc_params (ctx, start, ENOMEM); + return nullptr; + } + + + PIN->ctx = ctx; + PIN->params = start; + PIN->is_latlong = 0; + PIN->is_geocent = 0; + PIN->is_long_wrap_set = 0; + PIN->long_wrap_center = 0.0; + strcpy( PIN->axis, "enu" ); + + PIN->gridlist = NULL; + PIN->gridlist_count = 0; + + PIN->vgridlist_geoid = NULL; + PIN->vgridlist_geoid_count = 0; + + /* Set datum parameters. Similarly to +init parameters we want to expand */ + /* +datum parameters as late as possible when dealing with pipelines. */ + /* otherwise only the first occurrence of +datum will be expanded and that */ + if (n_pipelines == 0) { + if (pj_datum_set(ctx, start, PIN)) + return pj_default_destructor (PIN, proj_errno(PIN)); + } + + err = pj_ellipsoid (PIN); + + if (err) { + /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */ + if (PIN->need_ellps) { + pj_log (ctx, PJ_LOG_DEBUG_MINOR, "pj_init_ctx: Must specify ellipsoid or sphere"); + return pj_default_destructor (PIN, proj_errno(PIN)); + } + else { + if (PJD_ERR_MAJOR_AXIS_NOT_GIVEN==proj_errno (PIN)) + proj_errno_reset (PIN); + PIN->f = 1.0/298.257223563; + PIN->a_orig = PIN->a = 6378137.0; + PIN->es_orig = PIN->es = PIN->f*(2-PIN->f); + } + } + PIN->a_orig = PIN->a; + PIN->es_orig = PIN->es; + if (pj_calc_ellipsoid_params (PIN, PIN->a, PIN->es)) + return pj_default_destructor (PIN, PJD_ERR_ECCENTRICITY_IS_ONE); + + /* Now that we have ellipse information check for WGS84 datum */ + if( PIN->datum_type == PJD_3PARAM + && PIN->datum_params[0] == 0.0 + && PIN->datum_params[1] == 0.0 + && PIN->datum_params[2] == 0.0 + && PIN->a == 6378137.0 + && ABS(PIN->es - 0.006694379990) < 0.000000000050 )/*WGS84/GRS80*/ + { + PIN->datum_type = PJD_WGS84; + } + + /* Set PIN->geoc coordinate system */ + PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i); + + /* Over-ranging flag */ + PIN->over = pj_param(ctx, start, "bover").i; + + /* Vertical datum geoid grids */ + PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i; + if( PIN->has_geoid_vgrids ) /* we need to mark it as used. */ + pj_param(ctx, start, "sgeoidgrids"); + + /* Longitude center for wrapping */ + PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i; + if (PIN->is_long_wrap_set) { + PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f; + /* Don't accept excessive values otherwise we might perform badly */ + /* when correcting longitudes around it */ + /* The test is written this way to error on long_wrap_center "=" NaN */ + if( !(fabs(PIN->long_wrap_center) < 10 * M_TWOPI) ) + return pj_default_destructor (PIN, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT); + } + + /* Axis orientation */ + if( (pj_param(ctx, start,"saxis").s) != NULL ) + { + const char *axis_legal = "ewnsud"; + const char *axis_arg = pj_param(ctx, start,"saxis").s; + if( strlen(axis_arg) != 3 ) + return pj_default_destructor (PIN, PJD_ERR_AXIS); + + if( strchr( axis_legal, axis_arg[0] ) == NULL + || strchr( axis_legal, axis_arg[1] ) == NULL + || strchr( axis_legal, axis_arg[2] ) == NULL) + return pj_default_destructor (PIN, PJD_ERR_AXIS); + + /* TODO: it would be nice to validate we don't have on axis repeated */ + strcpy( PIN->axis, axis_arg ); + } + + /* Central meridian */ + PIN->lam0=pj_param(ctx, start, "rlon_0").f; + + /* Central latitude */ + PIN->phi0 = pj_param(ctx, start, "rlat_0").f; + + /* False easting and northing */ + PIN->x0 = pj_param(ctx, start, "dx_0").f; + PIN->y0 = pj_param(ctx, start, "dy_0").f; + PIN->z0 = pj_param(ctx, start, "dz_0").f; + PIN->t0 = pj_param(ctx, start, "dt_0").f; + + /* General scaling factor */ + if (pj_param(ctx, start, "tk_0").i) + PIN->k0 = pj_param(ctx, start, "dk_0").f; + else if (pj_param(ctx, start, "tk").i) + PIN->k0 = pj_param(ctx, start, "dk").f; + else + PIN->k0 = 1.; + if (PIN->k0 <= 0.) + return pj_default_destructor (PIN, PJD_ERR_K_LESS_THAN_ZERO); + + /* Set units */ + units = proj_list_units(); + s = 0; + if ((name = pj_param(ctx, start, "sunits").s) != NULL) { + for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; + if (!s) + return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); + s = units[i].to_meter; + } + if (s || (s = pj_param(ctx, start, "sto_meter").s)) { + double factor; + int ratio = 0; + + /* ratio number? */ + if (strlen (s) > 1 && s[0] == '1' && s[1]=='/') { + ratio = 1; + s += 2; + } + + factor = pj_strtod(s, 0); + if ((factor <= 0.0) || (1/factor==0)) + return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); + + PIN->to_meter = ratio? 1 / factor: factor; + PIN->fr_meter = 1 / PIN->to_meter; + + } else + PIN->to_meter = PIN->fr_meter = 1.; + + /* Set vertical units */ + s = 0; + if ((name = pj_param(ctx, start, "svunits").s) != NULL) { + for (i = 0; (s = units[i].id) && strcmp(name, s) ; ++i) ; + if (!s) + return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_UNIT_ID); + s = units[i].to_meter; + } + if (s || (s = pj_param(ctx, start, "svto_meter").s)) { + PIN->vto_meter = pj_strtod(s, 0); + if (*s == '/') /* ratio number */ + PIN->vto_meter /= pj_strtod(++s, 0); + if (PIN->vto_meter <= 0.0) + return pj_default_destructor (PIN, PJD_ERR_UNIT_FACTOR_LESS_THAN_0); + PIN->vfr_meter = 1. / PIN->vto_meter; + } else { + PIN->vto_meter = PIN->to_meter; + PIN->vfr_meter = PIN->fr_meter; + } + + /* Prime meridian */ + prime_meridians = proj_list_prime_meridians(); + s = 0; + if ((name = pj_param(ctx, start, "spm").s) != NULL) { + const char *value = NULL; + char *next_str = NULL; + + for (i = 0; prime_meridians[i].id != NULL; ++i ) + { + if( strcmp(name,prime_meridians[i].id) == 0 ) + { + value = prime_meridians[i].defn; + break; + } + } + + if( value == NULL + && (dmstor_ctx(ctx,name,&next_str) != 0.0 || *name == '0') + && *next_str == '\0' ) + value = name; + + if (!value) + return pj_default_destructor (PIN, PJD_ERR_UNKNOWN_PRIME_MERIDIAN); + PIN->from_greenwich = dmstor_ctx(ctx,value,NULL); + } + else + PIN->from_greenwich = 0.0; + + /* Private object for the geodesic functions */ + PIN->geod = static_cast(pj_calloc (1, sizeof (struct geod_geodesic))); + if (0==PIN->geod) + return pj_default_destructor (PIN, ENOMEM); + geod_init(PIN->geod, PIN->a, (1 - sqrt (1 - PIN->es))); + + /* Projection specific initialization */ + err = proj_errno_reset (PIN); + PIN = proj(PIN); + if (proj_errno (PIN)) { + pj_free(PIN); + return 0; + } + proj_errno_restore (PIN, err); + return PIN; +} diff --git a/src/pj_initcache.c b/src/pj_initcache.c deleted file mode 100644 index 3c347e4b..00000000 --- a/src/pj_initcache.c +++ /dev/null @@ -1,184 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: init file definition cache. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2009, Frank Warmerdam - * - * 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 - -#include "projects.h" - -static int cache_count = 0; -static int cache_alloc = 0; -static char **cache_key = NULL; -static paralist **cache_paralist = NULL; - -/************************************************************************/ -/* pj_clone_paralist() */ -/* */ -/* Allocate a copy of a parameter list. */ -/************************************************************************/ - -paralist *pj_clone_paralist( const paralist *list) -{ - paralist *list_copy = NULL, *next_copy = NULL; - - for( ; list != NULL; list = list->next ) - { - paralist *newitem = (paralist *) - pj_malloc(sizeof(paralist) + strlen(list->param)); - - newitem->used = 0; - newitem->next = 0; - strcpy( newitem->param, list->param ); - - if( list_copy == NULL ) - list_copy = newitem; - else - next_copy->next = newitem; - - next_copy = newitem; - } - - return list_copy; -} - -/************************************************************************/ -/* pj_clear_initcache() */ -/* */ -/* Clear out all memory held in the init file cache. */ -/************************************************************************/ - -void pj_clear_initcache() -{ - if( cache_alloc > 0 ) - { - int i; - - pj_acquire_lock(); - - for( i = 0; i < cache_count; i++ ) - { - paralist *n, *t = cache_paralist[i]; - - pj_dalloc( cache_key[i] ); - - /* free parameter list elements */ - for (; t != NULL; t = n) { - n = t->next; - pj_dalloc(t); - } - } - - pj_dalloc( cache_key ); - pj_dalloc( cache_paralist ); - cache_count = 0; - cache_alloc= 0; - cache_key = NULL; - cache_paralist = NULL; - - pj_release_lock(); - } -} - -/************************************************************************/ -/* pj_search_initcache() */ -/* */ -/* Search for a matching definition in the init cache. */ -/************************************************************************/ - -paralist *pj_search_initcache( const char *filekey ) - -{ - int i; - paralist *result = NULL; - - pj_acquire_lock(); - - for( i = 0; result == NULL && i < cache_count; i++) - { - if( strcmp(filekey,cache_key[i]) == 0 ) - { - result = pj_clone_paralist( cache_paralist[i] ); - } - } - - pj_release_lock(); - - return result; -} - -/************************************************************************/ -/* pj_insert_initcache() */ -/* */ -/* Insert a paralist definition in the init file cache. */ -/************************************************************************/ - -void pj_insert_initcache( const char *filekey, const paralist *list ) - -{ - pj_acquire_lock(); - - /* - ** Grow list if required. - */ - if( cache_count == cache_alloc ) - { - char **cache_key_new; - paralist **cache_paralist_new; - - cache_alloc = cache_alloc * 2 + 15; - - cache_key_new = (char **) pj_malloc(sizeof(char*) * cache_alloc); - if( cache_key && cache_count ) - { - memcpy( cache_key_new, cache_key, sizeof(char*) * cache_count); - } - pj_dalloc( cache_key ); - cache_key = cache_key_new; - - cache_paralist_new = (paralist **) - pj_malloc(sizeof(paralist*) * cache_alloc); - if( cache_paralist && cache_count ) - { - memcpy( cache_paralist_new, cache_paralist, - sizeof(paralist*) * cache_count ); - } - pj_dalloc( cache_paralist ); - cache_paralist = cache_paralist_new; - } - - /* - ** Duplicate the filekey and paralist, and insert in cache. - */ - cache_key[cache_count] = (char *) pj_malloc(strlen(filekey)+1); - strcpy( cache_key[cache_count], filekey ); - - cache_paralist[cache_count] = pj_clone_paralist( list ); - - cache_count++; - - pj_release_lock(); -} - diff --git a/src/pj_initcache.cpp b/src/pj_initcache.cpp new file mode 100644 index 00000000..3c347e4b --- /dev/null +++ b/src/pj_initcache.cpp @@ -0,0 +1,184 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: init file definition cache. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * 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 + +#include "projects.h" + +static int cache_count = 0; +static int cache_alloc = 0; +static char **cache_key = NULL; +static paralist **cache_paralist = NULL; + +/************************************************************************/ +/* pj_clone_paralist() */ +/* */ +/* Allocate a copy of a parameter list. */ +/************************************************************************/ + +paralist *pj_clone_paralist( const paralist *list) +{ + paralist *list_copy = NULL, *next_copy = NULL; + + for( ; list != NULL; list = list->next ) + { + paralist *newitem = (paralist *) + pj_malloc(sizeof(paralist) + strlen(list->param)); + + newitem->used = 0; + newitem->next = 0; + strcpy( newitem->param, list->param ); + + if( list_copy == NULL ) + list_copy = newitem; + else + next_copy->next = newitem; + + next_copy = newitem; + } + + return list_copy; +} + +/************************************************************************/ +/* pj_clear_initcache() */ +/* */ +/* Clear out all memory held in the init file cache. */ +/************************************************************************/ + +void pj_clear_initcache() +{ + if( cache_alloc > 0 ) + { + int i; + + pj_acquire_lock(); + + for( i = 0; i < cache_count; i++ ) + { + paralist *n, *t = cache_paralist[i]; + + pj_dalloc( cache_key[i] ); + + /* free parameter list elements */ + for (; t != NULL; t = n) { + n = t->next; + pj_dalloc(t); + } + } + + pj_dalloc( cache_key ); + pj_dalloc( cache_paralist ); + cache_count = 0; + cache_alloc= 0; + cache_key = NULL; + cache_paralist = NULL; + + pj_release_lock(); + } +} + +/************************************************************************/ +/* pj_search_initcache() */ +/* */ +/* Search for a matching definition in the init cache. */ +/************************************************************************/ + +paralist *pj_search_initcache( const char *filekey ) + +{ + int i; + paralist *result = NULL; + + pj_acquire_lock(); + + for( i = 0; result == NULL && i < cache_count; i++) + { + if( strcmp(filekey,cache_key[i]) == 0 ) + { + result = pj_clone_paralist( cache_paralist[i] ); + } + } + + pj_release_lock(); + + return result; +} + +/************************************************************************/ +/* pj_insert_initcache() */ +/* */ +/* Insert a paralist definition in the init file cache. */ +/************************************************************************/ + +void pj_insert_initcache( const char *filekey, const paralist *list ) + +{ + pj_acquire_lock(); + + /* + ** Grow list if required. + */ + if( cache_count == cache_alloc ) + { + char **cache_key_new; + paralist **cache_paralist_new; + + cache_alloc = cache_alloc * 2 + 15; + + cache_key_new = (char **) pj_malloc(sizeof(char*) * cache_alloc); + if( cache_key && cache_count ) + { + memcpy( cache_key_new, cache_key, sizeof(char*) * cache_count); + } + pj_dalloc( cache_key ); + cache_key = cache_key_new; + + cache_paralist_new = (paralist **) + pj_malloc(sizeof(paralist*) * cache_alloc); + if( cache_paralist && cache_count ) + { + memcpy( cache_paralist_new, cache_paralist, + sizeof(paralist*) * cache_count ); + } + pj_dalloc( cache_paralist ); + cache_paralist = cache_paralist_new; + } + + /* + ** Duplicate the filekey and paralist, and insert in cache. + */ + cache_key[cache_count] = (char *) pj_malloc(strlen(filekey)+1); + strcpy( cache_key[cache_count], filekey ); + + cache_paralist[cache_count] = pj_clone_paralist( list ); + + cache_count++; + + pj_release_lock(); +} + diff --git a/src/pj_internal.c b/src/pj_internal.c deleted file mode 100644 index 7eb917be..00000000 --- a/src/pj_internal.c +++ /dev/null @@ -1,445 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: This is primarily material originating from pj_obs_api.c - * (now proj_4D_api.c), that does not fit into the API - * category. Hence this pile of tubings and fittings for - * PROJ.4 internal plumbing. - * - * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05 - * - ****************************************************************************** - * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE - * - * 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 -#include -#include -#include -#include -#include -#include - -#include "geodesic.h" -#include "proj_internal.h" -#include "projects.h" - - -enum pj_io_units pj_left (PJ *P) { - enum pj_io_units u = P->inverted? P->right: P->left; - if (u==PJ_IO_UNITS_CLASSIC) - return PJ_IO_UNITS_PROJECTED; - return u; -} - -enum pj_io_units pj_right (PJ *P) { - enum pj_io_units u = P->inverted? P->left: P->right; - if (u==PJ_IO_UNITS_CLASSIC) - return PJ_IO_UNITS_PROJECTED; - return u; -} - - -/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */ -PJ_COORD proj_coord_error (void) { - PJ_COORD c; - c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL; - return c; -} - - - -/**************************************************************************************/ -PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { -/*************************************************************************************** -Behave mostly as proj_trans, but attempt to use 2D interfaces only. -Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement -chained calls starting out with a call to its 2D interface. -***************************************************************************************/ - if (0==P) - return coo; - if (P->inverted) - direction = -direction; - switch (direction) { - case PJ_FWD: - coo.xy = pj_fwd (coo.lp, P); - return coo; - case PJ_INV: - coo.lp = pj_inv (coo.xy, P); - return coo; - case PJ_IDENT: - return coo; - default: - break; - } - proj_errno_set (P, EINVAL); - return proj_coord_error (); -} - - -/**************************************************************************************/ -PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { -/*************************************************************************************** -Companion to pj_approx_2D_trans. - -Behave mostly as proj_trans, but attempt to use 3D interfaces only. -Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement -chained calls starting out with a call to its 3D interface. -***************************************************************************************/ - if (0==P) - return coo; - if (P->inverted) - direction = -direction; - switch (direction) { - case PJ_FWD: - coo.xyz = pj_fwd3d (coo.lpz, P); - return coo; - case PJ_INV: - coo.lpz = pj_inv3d (coo.xyz, P); - return coo; - case PJ_IDENT: - return coo; - default: - break; - } - proj_errno_set (P, EINVAL); - return proj_coord_error (); -} - -/**************************************************************************************/ -int pj_has_inverse(PJ *P) { -/*************************************************************************************** -Check if a a PJ has an inverse. -***************************************************************************************/ - return ( (P->inverted && (P->fwd || P->fwd3d || P->fwd4d) ) || - ( P->inv || P->inv3d || P->inv4d) ); -} - - -/* Move P to a new context - or to the default context if 0 is specified */ -void proj_context_set (PJ *P, PJ_CONTEXT *ctx) { - if (0==ctx) - ctx = pj_get_default_ctx (); - pj_set_ctx (P, ctx); -} - - -void proj_context_inherit (PJ *parent, PJ *child) { - if (0==parent) - pj_set_ctx (child, pj_get_default_ctx()); - else - pj_set_ctx (child, pj_get_ctx(parent)); -} - - - -/*****************************************************************************/ -char *pj_chomp (char *c) { -/****************************************************************************** -Strip pre- and postfix whitespace. Inline comments (indicated by '#') are -considered whitespace. -******************************************************************************/ - size_t i, n; - char *comment; - char *start = c; - - if (0==c) - return 0; - - comment = strchr (c, '#'); - if (comment) - *comment = 0; - - n = strlen (c); - if (0==n) - return c; - - /* Eliminate postfix whitespace */ - for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--) - c[i] = 0; - - /* Find start of non-whitespace */ - while (0 != *start && (';'==*start || isspace (*start))) - start++; - - n = strlen (start); - if (0==n) { - c[0] = 0; - return c; - } - - memmove (c, start, n + 1); - return c; -} - - - -/*****************************************************************************/ -char *pj_shrink (char *c) { -/****************************************************************************** -Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy, -consuming their surrounding whitespace. -******************************************************************************/ - size_t i, j, n; - - /* Flag showing that a whitespace (ws) has been written after last non-ws */ - size_t ws; - - if (0==c) - return 0; - - pj_chomp (c); - n = strlen (c); - - /* First collapse repeated whitespace (including +/;) */ - i = 0; - ws = 0; - for (j = 0; j < n; j++) { - - /* Eliminate prefix '+', only if preceded by whitespace */ - /* (i.e. keep it in 1.23e+08) */ - if ((i > 0) && ('+'==c[j]) && ws) - c[j] = ' '; - if ((i==0) && ('+'==c[j])) - c[j] = ' '; - - if (isspace (c[j]) || ';'==c[j]) { - if (0==ws && (i > 0)) - c[i++] = ' '; - ws = 1; - continue; - } - else { - ws = 0; - c[i++] = c[j]; - } - } - c[i] = 0; - n = strlen(c); - - /* Then make ',' and '=' greedy */ - i = 0; - for (j = 0; j < n; j++) { - if (i==0) { - c[i++] = c[j]; - continue; - } - - /* Skip space before '='/',' */ - if ('='==c[j] || ','==c[j]) { - if (c[i - 1]==' ') - c[i - 1] = c[j]; - else - c[i++] = c[j]; - continue; - } - - if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) ) - continue; - - c[i++] = c[j]; - } - c[i] = 0; - return c; -} - - - -/*****************************************************************************/ -size_t pj_trim_argc (char *args) { -/****************************************************************************** -Trim all unnecessary whitespace (and non-essential syntactic tokens) from the -argument string, args, and count its number of elements. -******************************************************************************/ - size_t i, m, n; - pj_shrink (args); - n = strlen (args); - if (n==0) - return 0; - for (i = m = 0; i < n; i++) { - if (' '==args[i]) { - args[i] = 0; - m++; - } - } - return m + 1; -} - - - -/*****************************************************************************/ -char **pj_trim_argv (size_t argc, char *args) { -/****************************************************************************** -Create an argv-style array from elements placed in the argument string, args. - -args is a trimmed string as returned by pj_trim_argc(), and argc is the number -of trimmed strings found (i.e. the return value of pj_trim_args()). Hence, - int argc = pj_trim_argc (args); - char **argv = pj_trim_argv (argc, args); -will produce a classic style (argc, argv) pair from a string of whitespace -separated args. No new memory is allocated for storing the individual args -(they stay in the args string), but for the pointers to the args a new array -is allocated and returned. - -It is the duty of the caller to free this array. -******************************************************************************/ - size_t i, j; - char **argv; - - if (0==args) - return 0; - if (0==argc) - return 0; - - - /* turn the input string into an array of strings */ - argv = (char **) calloc (argc, sizeof (char *)); - if (0==argv) - return 0; - argv[0] = args; - j = 1; - for (i = 0; ; i++) { - if (0==args[i]) { - argv[j++] = args + (i + 1); - } - if (j==argc) - break; - } - return argv; -} - - - -/*****************************************************************************/ -char *pj_make_args (size_t argc, char **argv) { -/****************************************************************************** -pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It -converts free format command line input to something proj_create can consume. - -Allocates, and returns, an array of char, large enough to hold a whitespace -separated copy of the args in argv. It is the duty of the caller to free this -array. -******************************************************************************/ - size_t i, n; - char *p; - - for (i = n = 0; i < argc; i++) - n += strlen (argv[i]); - - p = pj_calloc (n + argc + 1, sizeof (char)); - if (0==p) - return 0; - if (0==argc) - return p; - - for (i = 0; i < argc; i++) { - strcat (p, argv[i]); - strcat (p, " "); - } - return pj_shrink (p); -} - - - -/*****************************************************************************/ -void proj_context_errno_set (PJ_CONTEXT *ctx, int err) { -/****************************************************************************** -Raise an error directly on a context, without going through a PJ belonging -to that context. -******************************************************************************/ - if (0==ctx) - ctx = pj_get_default_ctx(); - pj_ctx_set_errno (ctx, err); -} - -/* logging */ - -/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */ -void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); - - -/***************************************************************************************/ -PJ_LOG_LEVEL proj_log_level (PJ_CONTEXT *ctx, PJ_LOG_LEVEL log_level) { -/**************************************************************************************** - Set logging level 0-3. Higher number means more debug info. 0 turns it off -****************************************************************************************/ - PJ_LOG_LEVEL previous; - if (0==ctx) - ctx = pj_get_default_ctx(); - if (0==ctx) - return PJ_LOG_TELL; - previous = abs (ctx->debug_level); - if (PJ_LOG_TELL==log_level) - return previous; - ctx->debug_level = log_level; - return previous; -} - - -/*****************************************************************************/ -void proj_log_error (PJ *P, const char *fmt, ...) { -/****************************************************************************** - For reporting the most severe events. -******************************************************************************/ - va_list args; - va_start( args, fmt ); - pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args); - va_end( args ); -} - - -/*****************************************************************************/ -void proj_log_debug (PJ *P, const char *fmt, ...) { -/****************************************************************************** - For reporting debugging information. -******************************************************************************/ - va_list args; - va_start( args, fmt ); - pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args); - va_end( args ); -} - - -/*****************************************************************************/ -void proj_log_trace (PJ *P, const char *fmt, ...) { -/****************************************************************************** - For reporting embarrasingly detailed debugging information. -******************************************************************************/ - va_list args; - va_start( args, fmt ); - pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args); - va_end( args ); -} - - -/*****************************************************************************/ -void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION logf) { -/****************************************************************************** - Put a new logging function into P's context. The opaque object app_data is - passed as first arg at each call to the logger -******************************************************************************/ - if (0==ctx) - pj_get_default_ctx (); - if (0==ctx) - return; - ctx->app_data = app_data; - if (0!=logf) - ctx->logger = logf; -} diff --git a/src/pj_internal.cpp b/src/pj_internal.cpp new file mode 100644 index 00000000..ac9fe1e0 --- /dev/null +++ b/src/pj_internal.cpp @@ -0,0 +1,445 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: This is primarily material originating from pj_obs_api.c + * (now proj_4D_api.c), that does not fit into the API + * category. Hence this pile of tubings and fittings for + * PROJ.4 internal plumbing. + * + * Author: Thomas Knudsen, thokn@sdfe.dk, 2017-07-05 + * + ****************************************************************************** + * Copyright (c) 2016, 2017, 2018, Thomas Knudsen/SDFE + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "geodesic.h" +#include "proj_internal.h" +#include "projects.h" + + +enum pj_io_units pj_left (PJ *P) { + enum pj_io_units u = P->inverted? P->right: P->left; + if (u==PJ_IO_UNITS_CLASSIC) + return PJ_IO_UNITS_PROJECTED; + return u; +} + +enum pj_io_units pj_right (PJ *P) { + enum pj_io_units u = P->inverted? P->left: P->right; + if (u==PJ_IO_UNITS_CLASSIC) + return PJ_IO_UNITS_PROJECTED; + return u; +} + + +/* Work around non-constness of MSVC HUGE_VAL by providing functions rather than constants */ +PJ_COORD proj_coord_error (void) { + PJ_COORD c; + c.v[0] = c.v[1] = c.v[2] = c.v[3] = HUGE_VAL; + return c; +} + + + +/**************************************************************************************/ +PJ_COORD pj_approx_2D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +/*************************************************************************************** +Behave mostly as proj_trans, but attempt to use 2D interfaces only. +Used in gie.c, to enforce testing 2D code, and by PJ_pipeline.c to implement +chained calls starting out with a call to its 2D interface. +***************************************************************************************/ + if (0==P) + return coo; + if (P->inverted) + direction = static_cast(-direction); + switch (direction) { + case PJ_FWD: + coo.xy = pj_fwd (coo.lp, P); + return coo; + case PJ_INV: + coo.lp = pj_inv (coo.xy, P); + return coo; + case PJ_IDENT: + return coo; + default: + break; + } + proj_errno_set (P, EINVAL); + return proj_coord_error (); +} + + +/**************************************************************************************/ +PJ_COORD pj_approx_3D_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coo) { +/*************************************************************************************** +Companion to pj_approx_2D_trans. + +Behave mostly as proj_trans, but attempt to use 3D interfaces only. +Used in gie.c, to enforce testing 3D code, and by PJ_pipeline.c to implement +chained calls starting out with a call to its 3D interface. +***************************************************************************************/ + if (0==P) + return coo; + if (P->inverted) + direction = static_cast(-direction); + switch (direction) { + case PJ_FWD: + coo.xyz = pj_fwd3d (coo.lpz, P); + return coo; + case PJ_INV: + coo.lpz = pj_inv3d (coo.xyz, P); + return coo; + case PJ_IDENT: + return coo; + default: + break; + } + proj_errno_set (P, EINVAL); + return proj_coord_error (); +} + +/**************************************************************************************/ +int pj_has_inverse(PJ *P) { +/*************************************************************************************** +Check if a a PJ has an inverse. +***************************************************************************************/ + return ( (P->inverted && (P->fwd || P->fwd3d || P->fwd4d) ) || + ( P->inv || P->inv3d || P->inv4d) ); +} + + +/* Move P to a new context - or to the default context if 0 is specified */ +void proj_context_set (PJ *P, PJ_CONTEXT *ctx) { + if (0==ctx) + ctx = pj_get_default_ctx (); + pj_set_ctx (P, ctx); +} + + +void proj_context_inherit (PJ *parent, PJ *child) { + if (0==parent) + pj_set_ctx (child, pj_get_default_ctx()); + else + pj_set_ctx (child, pj_get_ctx(parent)); +} + + + +/*****************************************************************************/ +char *pj_chomp (char *c) { +/****************************************************************************** +Strip pre- and postfix whitespace. Inline comments (indicated by '#') are +considered whitespace. +******************************************************************************/ + size_t i, n; + char *comment; + char *start = c; + + if (0==c) + return 0; + + comment = strchr (c, '#'); + if (comment) + *comment = 0; + + n = strlen (c); + if (0==n) + return c; + + /* Eliminate postfix whitespace */ + for (i = n - 1; (i > 0) && (isspace (c[i]) || ';'==c[i]); i--) + c[i] = 0; + + /* Find start of non-whitespace */ + while (0 != *start && (';'==*start || isspace (*start))) + start++; + + n = strlen (start); + if (0==n) { + c[0] = 0; + return c; + } + + memmove (c, start, n + 1); + return c; +} + + + +/*****************************************************************************/ +char *pj_shrink (char *c) { +/****************************************************************************** +Collapse repeated whitespace. Remove '+' and ';'. Make ',' and '=' greedy, +consuming their surrounding whitespace. +******************************************************************************/ + size_t i, j, n; + + /* Flag showing that a whitespace (ws) has been written after last non-ws */ + size_t ws; + + if (0==c) + return 0; + + pj_chomp (c); + n = strlen (c); + + /* First collapse repeated whitespace (including +/;) */ + i = 0; + ws = 0; + for (j = 0; j < n; j++) { + + /* Eliminate prefix '+', only if preceded by whitespace */ + /* (i.e. keep it in 1.23e+08) */ + if ((i > 0) && ('+'==c[j]) && ws) + c[j] = ' '; + if ((i==0) && ('+'==c[j])) + c[j] = ' '; + + if (isspace (c[j]) || ';'==c[j]) { + if (0==ws && (i > 0)) + c[i++] = ' '; + ws = 1; + continue; + } + else { + ws = 0; + c[i++] = c[j]; + } + } + c[i] = 0; + n = strlen(c); + + /* Then make ',' and '=' greedy */ + i = 0; + for (j = 0; j < n; j++) { + if (i==0) { + c[i++] = c[j]; + continue; + } + + /* Skip space before '='/',' */ + if ('='==c[j] || ','==c[j]) { + if (c[i - 1]==' ') + c[i - 1] = c[j]; + else + c[i++] = c[j]; + continue; + } + + if (' '==c[j] && ('='==c[i - 1] || ','==c[i - 1]) ) + continue; + + c[i++] = c[j]; + } + c[i] = 0; + return c; +} + + + +/*****************************************************************************/ +size_t pj_trim_argc (char *args) { +/****************************************************************************** +Trim all unnecessary whitespace (and non-essential syntactic tokens) from the +argument string, args, and count its number of elements. +******************************************************************************/ + size_t i, m, n; + pj_shrink (args); + n = strlen (args); + if (n==0) + return 0; + for (i = m = 0; i < n; i++) { + if (' '==args[i]) { + args[i] = 0; + m++; + } + } + return m + 1; +} + + + +/*****************************************************************************/ +char **pj_trim_argv (size_t argc, char *args) { +/****************************************************************************** +Create an argv-style array from elements placed in the argument string, args. + +args is a trimmed string as returned by pj_trim_argc(), and argc is the number +of trimmed strings found (i.e. the return value of pj_trim_args()). Hence, + int argc = pj_trim_argc (args); + char **argv = pj_trim_argv (argc, args); +will produce a classic style (argc, argv) pair from a string of whitespace +separated args. No new memory is allocated for storing the individual args +(they stay in the args string), but for the pointers to the args a new array +is allocated and returned. + +It is the duty of the caller to free this array. +******************************************************************************/ + size_t i, j; + char **argv; + + if (0==args) + return 0; + if (0==argc) + return 0; + + + /* turn the input string into an array of strings */ + argv = (char **) calloc (argc, sizeof (char *)); + if (0==argv) + return 0; + argv[0] = args; + j = 1; + for (i = 0; ; i++) { + if (0==args[i]) { + argv[j++] = args + (i + 1); + } + if (j==argc) + break; + } + return argv; +} + + + +/*****************************************************************************/ +char *pj_make_args (size_t argc, char **argv) { +/****************************************************************************** +pj_make_args is the inverse of the pj_trim_argc/pj_trim_argv combo: It +converts free format command line input to something proj_create can consume. + +Allocates, and returns, an array of char, large enough to hold a whitespace +separated copy of the args in argv. It is the duty of the caller to free this +array. +******************************************************************************/ + size_t i, n; + char *p; + + for (i = n = 0; i < argc; i++) + n += strlen (argv[i]); + + p = static_cast(pj_calloc (n + argc + 1, sizeof (char))); + if (0==p) + return 0; + if (0==argc) + return p; + + for (i = 0; i < argc; i++) { + strcat (p, argv[i]); + strcat (p, " "); + } + return pj_shrink (p); +} + + + +/*****************************************************************************/ +void proj_context_errno_set (PJ_CONTEXT *ctx, int err) { +/****************************************************************************** +Raise an error directly on a context, without going through a PJ belonging +to that context. +******************************************************************************/ + if (0==ctx) + ctx = pj_get_default_ctx(); + pj_ctx_set_errno (ctx, err); +} + +/* logging */ + +/* pj_vlog resides in pj_log.c and relates to pj_log as vsprintf relates to sprintf */ +void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); + + +/***************************************************************************************/ +PJ_LOG_LEVEL proj_log_level (PJ_CONTEXT *ctx, PJ_LOG_LEVEL log_level) { +/**************************************************************************************** + Set logging level 0-3. Higher number means more debug info. 0 turns it off +****************************************************************************************/ + PJ_LOG_LEVEL previous; + if (0==ctx) + ctx = pj_get_default_ctx(); + if (0==ctx) + return PJ_LOG_TELL; + previous = static_cast(abs (ctx->debug_level)); + if (PJ_LOG_TELL==log_level) + return previous; + ctx->debug_level = log_level; + return previous; +} + + +/*****************************************************************************/ +void proj_log_error (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting the most severe events. +******************************************************************************/ + va_list args; + va_start( args, fmt ); + pj_vlog (pj_get_ctx (P), PJ_LOG_ERROR , fmt, args); + va_end( args ); +} + + +/*****************************************************************************/ +void proj_log_debug (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting debugging information. +******************************************************************************/ + va_list args; + va_start( args, fmt ); + pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MAJOR , fmt, args); + va_end( args ); +} + + +/*****************************************************************************/ +void proj_log_trace (PJ *P, const char *fmt, ...) { +/****************************************************************************** + For reporting embarrasingly detailed debugging information. +******************************************************************************/ + va_list args; + va_start( args, fmt ); + pj_vlog (pj_get_ctx (P), PJ_LOG_DEBUG_MINOR , fmt, args); + va_end( args ); +} + + +/*****************************************************************************/ +void proj_log_func (PJ_CONTEXT *ctx, void *app_data, PJ_LOG_FUNCTION logf) { +/****************************************************************************** + Put a new logging function into P's context. The opaque object app_data is + passed as first arg at each call to the logger +******************************************************************************/ + if (0==ctx) + pj_get_default_ctx (); + if (0==ctx) + return; + ctx->app_data = app_data; + if (0!=logf) + ctx->logger = logf; +} diff --git a/src/pj_inv.c b/src/pj_inv.c deleted file mode 100644 index aaa8fea9..00000000 --- a/src/pj_inv.c +++ /dev/null @@ -1,238 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Inverse operation invocation - * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 - * Based on material from Gerald Evenden (original pj_inv) - * and Piyush Agram (original pj_inv3d) - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * Copyright (c) 2018, Thomas Knudsen / SDFE - * - * 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 -#include - -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" - -#define INPUT_UNITS P->right -#define OUTPUT_UNITS P->left - -static PJ_COORD inv_prepare (PJ *P, PJ_COORD coo) { - if (coo.v[0] == HUGE_VAL || coo.v[1] == HUGE_VAL || coo.v[2] == HUGE_VAL) { - proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); - return proj_coord_error (); - } - - /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ - if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; - if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; - - if (P->axisswap) - coo = proj_trans (P->axisswap, PJ_INV, coo); - - /* Handle remaining possible input types */ - switch (INPUT_UNITS) { - case PJ_IO_UNITS_WHATEVER: - return coo; - - /* de-scale and de-offset */ - case PJ_IO_UNITS_CARTESIAN: - coo.xyz.x *= P->to_meter; - coo.xyz.y *= P->to_meter; - coo.xyz.z *= P->to_meter; - if (P->is_geocent) { - coo = proj_trans (P->cart, PJ_INV, coo); - } - - return coo; - - case PJ_IO_UNITS_PROJECTED: - case PJ_IO_UNITS_CLASSIC: - coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; - coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; - coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0; - if (INPUT_UNITS==PJ_IO_UNITS_PROJECTED) - return coo; - - /* Classic proj.4 functions expect plane coordinates in units of the semimajor axis */ - /* Multiplying by ra, rather than dividing by a because the CalCOFI projection */ - /* stomps on a and hence (apparently) depends on this to roundtrip correctly */ - /* (CalCOFI avoids further scaling by stomping - but a better solution is possible) */ - coo.xyz.x *= P->ra; - coo.xyz.y *= P->ra; - return coo; - - case PJ_IO_UNITS_ANGULAR: - coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0; - break; - } - - /* Should not happen, so we could return pj_coord_err here */ - return coo; -} - - - -static PJ_COORD inv_finalize (PJ *P, PJ_COORD coo) { - if (coo.xyz.x == HUGE_VAL) { - proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); - return proj_coord_error (); - } - - if (OUTPUT_UNITS==PJ_IO_UNITS_ANGULAR) { - - /* Distance from central meridian, taking system zero meridian into account */ - coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; - - /* adjust longitude to central meridian */ - if (0==P->over) - coo.lpz.lam = adjlon(coo.lpz.lam); - - if (P->vgridshift) - coo = proj_trans (P->vgridshift, PJ_INV, coo); /* Go geometric from orthometric */ - if (coo.lp.lam==HUGE_VAL) - return coo; - if (P->hgridshift) - coo = proj_trans (P->hgridshift, PJ_FWD, coo); - else if (P->helmert || (P->cart_wgs84 != 0 && P->cart != 0)) { - coo = proj_trans (P->cart, PJ_FWD, coo); /* Go cartesian in local frame */ - if( P->helmert ) - coo = proj_trans (P->helmert, PJ_FWD, coo); /* Step into WGS84 */ - coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ - } - if (coo.lp.lam==HUGE_VAL) - return coo; - - /* If input latitude was geocentrical, convert back to geocentrical */ - if (P->geoc) - coo = pj_geocentric_latitude (P, PJ_FWD, coo); - } - - return coo; -} - - -static PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { - if (proj_errno(P)) - return proj_coord_error(); - - proj_errno_restore(P, last_errno); - return coord; -} - - -LP pj_inv(XY xy, PJ *P) { - int last_errno; - PJ_COORD coo = {{0,0,0,0}}; - coo.xy = xy; - - last_errno = proj_errno_reset(P); - - if (!P->skip_inv_prepare) - coo = inv_prepare (P, coo); - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().lp; - - /* Do the transformation, using the lowest dimensional transformer available */ - if (P->inv) - coo.lp = P->inv(coo.xy, P); - else if (P->inv3d) - coo.lpz = P->inv3d (coo.xyz, P); - else if (P->inv4d) - coo = P->inv4d (coo, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error ().lp; - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().lp; - - if (!P->skip_inv_finalize) - coo = inv_finalize (P, coo); - - return error_or_coord(P, coo, last_errno).lp; -} - - - -LPZ pj_inv3d (XYZ xyz, PJ *P) { - int last_errno; - PJ_COORD coo = {{0,0,0,0}}; - coo.xyz = xyz; - - last_errno = proj_errno_reset(P); - - if (!P->skip_inv_prepare) - coo = inv_prepare (P, coo); - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().lpz; - - /* Do the transformation, using the lowest dimensional transformer feasible */ - if (P->inv3d) - coo.lpz = P->inv3d (coo.xyz, P); - else if (P->inv4d) - coo = P->inv4d (coo, P); - else if (P->inv) - coo.lp = P->inv (coo.xy, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error ().lpz; - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error ().lpz; - - if (!P->skip_inv_finalize) - coo = inv_finalize (P, coo); - - return error_or_coord(P, coo, last_errno).lpz; -} - - - -PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { - int last_errno = proj_errno_reset(P); - - if (!P->skip_inv_prepare) - coo = inv_prepare (P, coo); - if (HUGE_VAL==coo.v[0]) - return proj_coord_error (); - - /* Call the highest dimensional converter available */ - if (P->inv4d) - coo = P->inv4d (coo, P); - else if (P->inv3d) - coo.lpz = P->inv3d (coo.xyz, P); - else if (P->inv) - coo.lp = P->inv (coo.xy, P); - else { - proj_errno_set (P, EINVAL); - return proj_coord_error (); - } - if (HUGE_VAL==coo.v[0]) - return proj_coord_error (); - - if (!P->skip_inv_finalize) - coo = inv_finalize (P, coo); - - return error_or_coord(P, coo, last_errno); -} diff --git a/src/pj_inv.cpp b/src/pj_inv.cpp new file mode 100644 index 00000000..aaa8fea9 --- /dev/null +++ b/src/pj_inv.cpp @@ -0,0 +1,238 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Inverse operation invocation + * Author: Thomas Knudsen, thokn@sdfe.dk, 2018-01-02 + * Based on material from Gerald Evenden (original pj_inv) + * and Piyush Agram (original pj_inv3d) + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2018, Thomas Knudsen / SDFE + * + * 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 +#include + +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" + +#define INPUT_UNITS P->right +#define OUTPUT_UNITS P->left + +static PJ_COORD inv_prepare (PJ *P, PJ_COORD coo) { + if (coo.v[0] == HUGE_VAL || coo.v[1] == HUGE_VAL || coo.v[2] == HUGE_VAL) { + proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); + return proj_coord_error (); + } + + /* The helmert datum shift will choke unless it gets a sensible 4D coordinate */ + if (HUGE_VAL==coo.v[2] && P->helmert) coo.v[2] = 0.0; + if (HUGE_VAL==coo.v[3] && P->helmert) coo.v[3] = 0.0; + + if (P->axisswap) + coo = proj_trans (P->axisswap, PJ_INV, coo); + + /* Handle remaining possible input types */ + switch (INPUT_UNITS) { + case PJ_IO_UNITS_WHATEVER: + return coo; + + /* de-scale and de-offset */ + case PJ_IO_UNITS_CARTESIAN: + coo.xyz.x *= P->to_meter; + coo.xyz.y *= P->to_meter; + coo.xyz.z *= P->to_meter; + if (P->is_geocent) { + coo = proj_trans (P->cart, PJ_INV, coo); + } + + return coo; + + case PJ_IO_UNITS_PROJECTED: + case PJ_IO_UNITS_CLASSIC: + coo.xyz.x = P->to_meter * coo.xyz.x - P->x0; + coo.xyz.y = P->to_meter * coo.xyz.y - P->y0; + coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0; + if (INPUT_UNITS==PJ_IO_UNITS_PROJECTED) + return coo; + + /* Classic proj.4 functions expect plane coordinates in units of the semimajor axis */ + /* Multiplying by ra, rather than dividing by a because the CalCOFI projection */ + /* stomps on a and hence (apparently) depends on this to roundtrip correctly */ + /* (CalCOFI avoids further scaling by stomping - but a better solution is possible) */ + coo.xyz.x *= P->ra; + coo.xyz.y *= P->ra; + return coo; + + case PJ_IO_UNITS_ANGULAR: + coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0; + break; + } + + /* Should not happen, so we could return pj_coord_err here */ + return coo; +} + + + +static PJ_COORD inv_finalize (PJ *P, PJ_COORD coo) { + if (coo.xyz.x == HUGE_VAL) { + proj_errno_set (P, PJD_ERR_INVALID_X_OR_Y); + return proj_coord_error (); + } + + if (OUTPUT_UNITS==PJ_IO_UNITS_ANGULAR) { + + /* Distance from central meridian, taking system zero meridian into account */ + coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0; + + /* adjust longitude to central meridian */ + if (0==P->over) + coo.lpz.lam = adjlon(coo.lpz.lam); + + if (P->vgridshift) + coo = proj_trans (P->vgridshift, PJ_INV, coo); /* Go geometric from orthometric */ + if (coo.lp.lam==HUGE_VAL) + return coo; + if (P->hgridshift) + coo = proj_trans (P->hgridshift, PJ_FWD, coo); + else if (P->helmert || (P->cart_wgs84 != 0 && P->cart != 0)) { + coo = proj_trans (P->cart, PJ_FWD, coo); /* Go cartesian in local frame */ + if( P->helmert ) + coo = proj_trans (P->helmert, PJ_FWD, coo); /* Step into WGS84 */ + coo = proj_trans (P->cart_wgs84, PJ_INV, coo); /* Go back to angular using WGS84 ellps */ + } + if (coo.lp.lam==HUGE_VAL) + return coo; + + /* If input latitude was geocentrical, convert back to geocentrical */ + if (P->geoc) + coo = pj_geocentric_latitude (P, PJ_FWD, coo); + } + + return coo; +} + + +static PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) { + if (proj_errno(P)) + return proj_coord_error(); + + proj_errno_restore(P, last_errno); + return coord; +} + + +LP pj_inv(XY xy, PJ *P) { + int last_errno; + PJ_COORD coo = {{0,0,0,0}}; + coo.xy = xy; + + last_errno = proj_errno_reset(P); + + if (!P->skip_inv_prepare) + coo = inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lp; + + /* Do the transformation, using the lowest dimensional transformer available */ + if (P->inv) + coo.lp = P->inv(coo.xy, P); + else if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv4d) + coo = P->inv4d (coo, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().lp; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lp; + + if (!P->skip_inv_finalize) + coo = inv_finalize (P, coo); + + return error_or_coord(P, coo, last_errno).lp; +} + + + +LPZ pj_inv3d (XYZ xyz, PJ *P) { + int last_errno; + PJ_COORD coo = {{0,0,0,0}}; + coo.xyz = xyz; + + last_errno = proj_errno_reset(P); + + if (!P->skip_inv_prepare) + coo = inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lpz; + + /* Do the transformation, using the lowest dimensional transformer feasible */ + if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv4d) + coo = P->inv4d (coo, P); + else if (P->inv) + coo.lp = P->inv (coo.xy, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error ().lpz; + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error ().lpz; + + if (!P->skip_inv_finalize) + coo = inv_finalize (P, coo); + + return error_or_coord(P, coo, last_errno).lpz; +} + + + +PJ_COORD pj_inv4d (PJ_COORD coo, PJ *P) { + int last_errno = proj_errno_reset(P); + + if (!P->skip_inv_prepare) + coo = inv_prepare (P, coo); + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); + + /* Call the highest dimensional converter available */ + if (P->inv4d) + coo = P->inv4d (coo, P); + else if (P->inv3d) + coo.lpz = P->inv3d (coo.xyz, P); + else if (P->inv) + coo.lp = P->inv (coo.xy, P); + else { + proj_errno_set (P, EINVAL); + return proj_coord_error (); + } + if (HUGE_VAL==coo.v[0]) + return proj_coord_error (); + + if (!P->skip_inv_finalize) + coo = inv_finalize (P, coo); + + return error_or_coord(P, coo, last_errno); +} diff --git a/src/pj_list.c b/src/pj_list.c deleted file mode 100644 index 33313875..00000000 --- a/src/pj_list.c +++ /dev/null @@ -1,32 +0,0 @@ -/* Projection System: default list of projections -** Use local definition of PJ_LIST_H for subset. -*/ - -#include "proj.h" - -#define USE_PJ_LIST_H 1 -#include "projects.h" - - -/* Generate prototypes for projection functions */ -#define PROJ_HEAD(id, name) struct PJconsts *pj_##id(struct PJconsts*); -#include "pj_list.h" -#undef PROJ_HEAD - -/* Generate extern declarations for description strings */ -#define PROJ_HEAD(id, name) extern const char * const pj_s_##id; -#include "pj_list.h" -#undef PROJ_HEAD - -/* Generate the null-terminated list of projection functions with associated mnemonics and descriptions */ -#define PROJ_HEAD(id, name) {#id, pj_##id, &pj_s_##id}, -const struct PJ_LIST pj_list[] = { -#include "pj_list.h" - {0, 0, 0}, -}; -#undef PROJ_HEAD - - -const PJ_OPERATIONS *proj_list_operations(void) { - return pj_list; -} diff --git a/src/pj_list.cpp b/src/pj_list.cpp new file mode 100644 index 00000000..55ea36c2 --- /dev/null +++ b/src/pj_list.cpp @@ -0,0 +1,32 @@ +/* Projection System: default list of projections +** Use local definition of PJ_LIST_H for subset. +*/ + +#include "proj.h" + +#define USE_PJ_LIST_H 1 +#include "projects.h" + + +/* Generate prototypes for projection functions */ +#define PROJ_HEAD(id, name) extern "C" struct PJconsts *pj_##id(struct PJconsts*); +#include "pj_list.h" +#undef PROJ_HEAD + +/* Generate extern declarations for description strings */ +#define PROJ_HEAD(id, name) extern "C" const char * const pj_s_##id; +#include "pj_list.h" +#undef PROJ_HEAD + +/* Generate the null-terminated list of projection functions with associated mnemonics and descriptions */ +#define PROJ_HEAD(id, name) {#id, pj_##id, &pj_s_##id}, +const struct PJ_LIST pj_list[] = { +#include "pj_list.h" + {0, 0, 0}, +}; +#undef PROJ_HEAD + + +const PJ_OPERATIONS *proj_list_operations(void) { + return pj_list; +} diff --git a/src/pj_log.c b/src/pj_log.c deleted file mode 100644 index 6654691c..00000000 --- a/src/pj_log.c +++ /dev/null @@ -1,98 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of pj_log() function. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2010, Frank Warmerdam - * - * 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 -#include -#include -#include - -#include "proj.h" -#include "projects.h" - -/************************************************************************/ -/* pj_stderr_logger() */ -/************************************************************************/ - -void pj_stderr_logger( void *app_data, int level, const char *msg ) - -{ - (void) app_data; - (void) level; - fprintf( stderr, "%s\n", msg ); -} - -/************************************************************************/ -/* pj_vlog() */ -/************************************************************************/ -void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); -/* Workhorse for the log functions - relates to pj_log as vsprintf relates to sprintf */ -void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ) - -{ - char *msg_buf; - int debug_level = ctx->debug_level; - int shutup_unless_errno_set = debug_level < 0; - - /* For negative debug levels, we first start logging when errno is set */ - if (ctx->last_errno==0 && shutup_unless_errno_set) - return; - - if (debug_level < 0) - debug_level = -debug_level; - - if( level > debug_level ) - return; - - msg_buf = (char *) malloc(100000); - if( msg_buf == NULL ) - return; - - /* we should use vsnprintf where available once we add configure detect.*/ - vsprintf( msg_buf, fmt, args ); - - ctx->logger( ctx->app_data, level, msg_buf ); - - free( msg_buf ); -} - - -/************************************************************************/ -/* pj_log() */ -/************************************************************************/ - -void pj_log( projCtx ctx, int level, const char *fmt, ... ) - -{ - va_list args; - - if( level > ctx->debug_level ) - return; - - va_start( args, fmt ); - pj_vlog( ctx, level, fmt, args ); - va_end( args ); -} diff --git a/src/pj_log.cpp b/src/pj_log.cpp new file mode 100644 index 00000000..6654691c --- /dev/null +++ b/src/pj_log.cpp @@ -0,0 +1,98 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of pj_log() function. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2010, Frank Warmerdam + * + * 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 +#include +#include +#include + +#include "proj.h" +#include "projects.h" + +/************************************************************************/ +/* pj_stderr_logger() */ +/************************************************************************/ + +void pj_stderr_logger( void *app_data, int level, const char *msg ) + +{ + (void) app_data; + (void) level; + fprintf( stderr, "%s\n", msg ); +} + +/************************************************************************/ +/* pj_vlog() */ +/************************************************************************/ +void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ); +/* Workhorse for the log functions - relates to pj_log as vsprintf relates to sprintf */ +void pj_vlog( projCtx ctx, int level, const char *fmt, va_list args ) + +{ + char *msg_buf; + int debug_level = ctx->debug_level; + int shutup_unless_errno_set = debug_level < 0; + + /* For negative debug levels, we first start logging when errno is set */ + if (ctx->last_errno==0 && shutup_unless_errno_set) + return; + + if (debug_level < 0) + debug_level = -debug_level; + + if( level > debug_level ) + return; + + msg_buf = (char *) malloc(100000); + if( msg_buf == NULL ) + return; + + /* we should use vsnprintf where available once we add configure detect.*/ + vsprintf( msg_buf, fmt, args ); + + ctx->logger( ctx->app_data, level, msg_buf ); + + free( msg_buf ); +} + + +/************************************************************************/ +/* pj_log() */ +/************************************************************************/ + +void pj_log( projCtx ctx, int level, const char *fmt, ... ) + +{ + va_list args; + + if( level > ctx->debug_level ) + return; + + va_start( args, fmt ); + pj_vlog( ctx, level, fmt, args ); + va_end( args ); +} diff --git a/src/pj_malloc.c b/src/pj_malloc.c deleted file mode 100644 index c45da85d..00000000 --- a/src/pj_malloc.c +++ /dev/null @@ -1,239 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Memory management for proj.4. - * This version includes an implementation of generic destructors, - * for memory deallocation for the large majority of PJ-objects - * that do not allocate anything else than the PJ-object itself, - * and its associated opaque object - i.e. no additional malloc'ed - * memory inside the opaque object. - * - * Author: Gerald I. Evenden (Original proj.4 author), - * Frank Warmerdam (2000) pj_malloc? - * Thomas Knudsen (2016) - freeup/dealloc parts - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * Copyright (c) 2016, Thomas Knudsen / SDFE - * - * 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. - *****************************************************************************/ - -/* allocate and deallocate memory */ -/* These routines are used so that applications can readily replace -** projection system memory allocation/deallocation call with custom -** application procedures. */ - -#include -#include -#include -#include - -#include "proj.h" -#include "projects.h" - -/**********************************************************************/ -void *pj_malloc(size_t size) { -/*********************************************************************** -Currently, pj_malloc is a hack to solve an errno problem. -The problem is described in more details at -https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=86420. -It seems, that pj_init and similar functions incorrectly -(under debian/glibs-2.3.2) assume that pj_malloc resets -errno after success. pj_malloc tries to mimic this. - -NOTE (2017-09-29): The problem described at the bugzilla page -referred to above, is most likely a case of someone not -understanding the proper usage of errno. We should review -whether "the problem is actually a problem" in PROJ.4 code. - -Library specific allocators can be useful, and improve -interoperability, if properly used. That is, by making them -run/initialization time switchable, somewhat like the file i/o -interface. - -But as things stand, we are more likely to get benefit -from reviewing the code for proper errno usage, which is hard, -due to the presence of context local and global pj_errnos. - -Probably, these were introduced in order to support incomplete -implementations of thread local errnos at an early phase of the -implementation of multithreading support in PROJ.4). - -It is likely too late to get rid of contexts, but we can still -benefit from a better usage of errno. -***********************************************************************/ - int old_errno = errno; - void *res = malloc(size); - if ( res && !old_errno ) - errno = 0; - return res; -} - - -/**********************************************************************/ -void *pj_calloc (size_t n, size_t size) { -/*********************************************************************** -pj_calloc is the pj-equivalent of calloc(). - -It allocates space for an array of elements of size . -The array is initialized to zeros. -***********************************************************************/ - void *res = pj_malloc (n*size); - if (0==res) - return 0; - memset (res, 0, n*size); - return res; -} - - -/**********************************************************************/ -void pj_dalloc(void *ptr) { -/**********************************************************************/ - free(ptr); -} - - -/**********************************************************************/ -void *pj_dealloc (void *ptr) { -/*********************************************************************** -pj_dealloc supports the common use case of "clean up and return a null -pointer" to signal an error in a multi level allocation: - - struct foo { int bar; int *baz; }; - - struct foo *p = pj_calloc (1, sizeof (struct foo)); - if (0==p) - return 0; - - p->baz = pj_calloc (10, sizeof(int)); - if (0==p->baz) - return pj_dealloc (p); // clean up + signal error by 0-return - - return p; // success - -***********************************************************************/ - if (0==ptr) - return 0; - pj_dalloc (ptr); - return 0; -} - -/**********************************************************************/ -char *pj_strdup(const char *str) -/**********************************************************************/ -{ - size_t len = strlen(str) + 1; - char *dup = pj_malloc(len); - if (dup) - memcpy(dup, str, len); - return dup; -} - - -/*****************************************************************************/ -void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) { -/***************************************************************************** - Companion to pj_default_destructor (below). Deallocates a linked list - of "+proj=xxx" initialization parameters. - - Also called from pj_init_ctx when encountering errors before the PJ - proper is allocated. -******************************************************************************/ - paralist *t, *n; - for (t = start; t; t = n) { - n = t->next; - pj_dealloc(t); - } - pj_ctx_set_errno (ctx, errlev); - return (void *) 0; -} - - - - -/************************************************************************/ -/* pj_free() */ -/* */ -/* This is the application callable entry point for destroying */ -/* a projection definition. It does work generic to all */ -/* projection types, and then calls the projection specific */ -/* free function, P->destructor(), to do local work. */ -/* In most cases P->destructor()==pj_default_destructor. */ -/************************************************************************/ - -void pj_free(PJ *P) { - if (0==P) - return; - /* free projection parameters - all the hard work is done by */ - /* pj_default_destructor, which is supposed */ - /* to be called as the last step of the local destructor */ - /* pointed to by P->destructor. In most cases, */ - /* pj_default_destructor actually *is* what is pointed to */ - P->destructor (P, proj_errno(P)); -} - - - - -/*****************************************************************************/ -void *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ -/***************************************************************************** - Does memory deallocation for "plain" PJ objects, i.e. that vast majority - of PJs where the opaque object does not contain any additionally - allocated memory below the P->opaque level. -******************************************************************************/ - - /* Even if P==0, we set the errlev on pj_error and the default context */ - /* Note that both, in the multithreaded case, may then contain undefined */ - /* values. This is expected behaviour. For MT have one ctx per thread */ - if (0!=errlev) - pj_ctx_set_errno (pj_get_ctx(P), errlev); - - if (0==P) - return 0; - - /* free grid lists */ - pj_dealloc( P->gridlist ); - pj_dealloc( P->vgridlist_geoid ); - pj_dealloc( P->catalog_name ); - - /* We used to call pj_dalloc( P->catalog ), but this will leak */ - /* memory. The safe way to clear catalog and grid is to call */ - /* pj_gc_unloadall(pj_get_default_ctx()); and pj_deallocate_grids(); */ - /* TODO: we should probably have a public pj_cleanup() method to do all */ - /* that */ - - /* free the interface to Charles Karney's geodesic library */ - pj_dealloc( P->geod ); - - /* free parameter list elements */ - pj_dealloc_params (pj_get_ctx(P), P->params, errlev); - pj_dealloc (P->def_full); - - /* free the cs2cs emulation elements */ - pj_free (P->axisswap); - pj_free (P->helmert); - pj_free (P->cart); - pj_free (P->cart_wgs84); - pj_free (P->hgridshift); - pj_free (P->vgridshift); - - pj_dealloc (P->opaque); - return pj_dealloc(P); -} diff --git a/src/pj_malloc.cpp b/src/pj_malloc.cpp new file mode 100644 index 00000000..66977cf4 --- /dev/null +++ b/src/pj_malloc.cpp @@ -0,0 +1,240 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Memory management for proj.4. + * This version includes an implementation of generic destructors, + * for memory deallocation for the large majority of PJ-objects + * that do not allocate anything else than the PJ-object itself, + * and its associated opaque object - i.e. no additional malloc'ed + * memory inside the opaque object. + * + * Author: Gerald I. Evenden (Original proj.4 author), + * Frank Warmerdam (2000) pj_malloc? + * Thomas Knudsen (2016) - freeup/dealloc parts + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * Copyright (c) 2016, Thomas Knudsen / SDFE + * + * 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. + *****************************************************************************/ + +/* allocate and deallocate memory */ +/* These routines are used so that applications can readily replace +** projection system memory allocation/deallocation call with custom +** application procedures. */ + +#include +#include +#include +#include + +#include "proj.h" +#include "projects.h" + +/**********************************************************************/ +void *pj_malloc(size_t size) { +/*********************************************************************** +Currently, pj_malloc is a hack to solve an errno problem. +The problem is described in more details at +https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=86420. +It seems, that pj_init and similar functions incorrectly +(under debian/glibs-2.3.2) assume that pj_malloc resets +errno after success. pj_malloc tries to mimic this. + +NOTE (2017-09-29): The problem described at the bugzilla page +referred to above, is most likely a case of someone not +understanding the proper usage of errno. We should review +whether "the problem is actually a problem" in PROJ.4 code. + +Library specific allocators can be useful, and improve +interoperability, if properly used. That is, by making them +run/initialization time switchable, somewhat like the file i/o +interface. + +But as things stand, we are more likely to get benefit +from reviewing the code for proper errno usage, which is hard, +due to the presence of context local and global pj_errnos. + +Probably, these were introduced in order to support incomplete +implementations of thread local errnos at an early phase of the +implementation of multithreading support in PROJ.4). + +It is likely too late to get rid of contexts, but we can still +benefit from a better usage of errno. +***********************************************************************/ + int old_errno = errno; + void *res = malloc(size); + if ( res && !old_errno ) + errno = 0; + return res; +} + + +/**********************************************************************/ +void *pj_calloc (size_t n, size_t size) { +/*********************************************************************** +pj_calloc is the pj-equivalent of calloc(). + +It allocates space for an array of elements of size . +The array is initialized to zeros. +***********************************************************************/ + void *res = pj_malloc (n*size); + if (0==res) + return 0; + memset (res, 0, n*size); + return res; +} + + +/**********************************************************************/ +void pj_dalloc(void *ptr) { +/**********************************************************************/ + free(ptr); +} + + +/**********************************************************************/ +void *pj_dealloc (void *ptr) { +/*********************************************************************** +pj_dealloc supports the common use case of "clean up and return a null +pointer" to signal an error in a multi level allocation: + + struct foo { int bar; int *baz; }; + + struct foo *p = pj_calloc (1, sizeof (struct foo)); + if (0==p) + return 0; + + p->baz = pj_calloc (10, sizeof(int)); + if (0==p->baz) + return pj_dealloc (p); // clean up + signal error by 0-return + + return p; // success + +***********************************************************************/ + if (0==ptr) + return 0; + pj_dalloc (ptr); + return 0; +} + +/**********************************************************************/ +char *pj_strdup(const char *str) +/**********************************************************************/ +{ + size_t len = strlen(str) + 1; + char *dup = static_cast(pj_malloc(len)); + if (dup) + memcpy(dup, str, len); + return dup; +} + + +/*****************************************************************************/ +void *pj_dealloc_params (PJ_CONTEXT *ctx, paralist *start, int errlev) { +/***************************************************************************** + Companion to pj_default_destructor (below). Deallocates a linked list + of "+proj=xxx" initialization parameters. + + Also called from pj_init_ctx when encountering errors before the PJ + proper is allocated. +******************************************************************************/ + paralist *t, *n; + for (t = start; t; t = n) { + n = t->next; + pj_dealloc(t); + } + pj_ctx_set_errno (ctx, errlev); + return (void *) 0; +} + + + + +/************************************************************************/ +/* pj_free() */ +/* */ +/* This is the application callable entry point for destroying */ +/* a projection definition. It does work generic to all */ +/* projection types, and then calls the projection specific */ +/* free function, P->destructor(), to do local work. */ +/* In most cases P->destructor()==pj_default_destructor. */ +/************************************************************************/ + +void pj_free(PJ *P) { + if (0==P) + return; + /* free projection parameters - all the hard work is done by */ + /* pj_default_destructor, which is supposed */ + /* to be called as the last step of the local destructor */ + /* pointed to by P->destructor. In most cases, */ + /* pj_default_destructor actually *is* what is pointed to */ + P->destructor (P, proj_errno(P)); +} + + + + +/*****************************************************************************/ +PJ *pj_default_destructor (PJ *P, int errlev) { /* Destructor */ +/***************************************************************************** + Does memory deallocation for "plain" PJ objects, i.e. that vast majority + of PJs where the opaque object does not contain any additionally + allocated memory below the P->opaque level. +******************************************************************************/ + + /* Even if P==0, we set the errlev on pj_error and the default context */ + /* Note that both, in the multithreaded case, may then contain undefined */ + /* values. This is expected behaviour. For MT have one ctx per thread */ + if (0!=errlev) + pj_ctx_set_errno (pj_get_ctx(P), errlev); + + if (0==P) + return 0; + + /* free grid lists */ + pj_dealloc( P->gridlist ); + pj_dealloc( P->vgridlist_geoid ); + pj_dealloc( P->catalog_name ); + + /* We used to call pj_dalloc( P->catalog ), but this will leak */ + /* memory. The safe way to clear catalog and grid is to call */ + /* pj_gc_unloadall(pj_get_default_ctx()); and pj_deallocate_grids(); */ + /* TODO: we should probably have a public pj_cleanup() method to do all */ + /* that */ + + /* free the interface to Charles Karney's geodesic library */ + pj_dealloc( P->geod ); + + /* free parameter list elements */ + pj_dealloc_params (pj_get_ctx(P), P->params, errlev); + pj_dealloc (P->def_full); + + /* free the cs2cs emulation elements */ + pj_free (P->axisswap); + pj_free (P->helmert); + pj_free (P->cart); + pj_free (P->cart_wgs84); + pj_free (P->hgridshift); + pj_free (P->vgridshift); + + pj_dealloc (static_cast(P->opaque)); + pj_dealloc(P); + return nullptr; +} diff --git a/src/pj_math.c b/src/pj_math.c deleted file mode 100644 index 540ab9eb..00000000 --- a/src/pj_math.c +++ /dev/null @@ -1,108 +0,0 @@ -/****************************************************************************** - * Project: PROJ - * Purpose: Make C99 math functions available on C89 systems - * Author: Kristian Evers - * - ****************************************************************************** - * Copyright (c) 2018, Kristian Evers - * - * 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 "proj_math.h" - -/* pj_isnan is used in gie.c which means that is has to */ -/* be exported in the Windows DLL and therefore needs */ -/* to be declared even though we have isnan() on the */ -/* system. */ - -#ifdef HAVE_C99_MATH -int pj_isnan (double x); -#endif - -/* Returns 0 if not a NaN and non-zero if val is a NaN */ -int pj_isnan (double x) { - /* cppcheck-suppress duplicateExpression */ - return x != x; -} - -#if !(defined(HAVE_C99_MATH) && HAVE_C99_MATH) - -/* Compute hypotenuse */ -double pj_hypot(double x, double y) { - x = fabs(x); - y = fabs(y); - if ( x < y ) { - x /= y; - return ( y * sqrt( 1. + x * x ) ); - } else { - y /= (x != 0.0 ? x : 1.0); - return ( x * sqrt( 1. + y * y ) ); - } -} - -/* Compute log(1+x) accurately */ -double pj_log1p(double x) { - volatile double - y = 1 + x, - z = y - 1; - /* Here's the explanation for this magic: y = 1 + z, exactly, and z - * approx x, thus log(y)/z (which is nearly constant near z = 0) returns - * a good approximation to the true log(1 + x)/x. The multiplication x * - * (log(y)/z) introduces little additional error. */ - return z == 0 ? x : x * log(y) / z; -} - -/* Compute asinh(x) accurately */ -double pj_asinh(double x) { - double y = fabs(x); /* Enforce odd parity */ - y = log1p(y * (1 + y/(hypot(1.0, y) + 1))); - return x > 0 ? y : (x < 0 ? -y : x); -} - -double pj_round(double x) { - /* The handling of corner cases is copied from boost; see - * https://github.com/boostorg/math/pull/8 - * with improvements to return -0.0 when appropriate */ - double t; - if (x == 0) - return x; /* Retain sign of 0 */ - else if (0 < x && x < 0.5) - return +0.0; - else if (0 > x && x > -0.5) - return -0.0; - else if (x > 0) { - t = ceil(x); - return 0.5 < t - x ? t - 1 : t; - } else { /* Includes NaN */ - t = floor(x); - return 0.5 < x - t ? t + 1 : t; - } -} - -long pj_lround(double x) { - /* Default value for overflow + NaN + (x == LONG_MIN) */ - long r = LONG_MIN; - x = round(x); - if (fabs(x) < -(double)LONG_MIN) /* Assume (double)LONG_MIN is exact */ - r = (int)x; - return r; -} - -#endif /* !(defined(HAVE_C99_MATH) && HAVE_C99_MATH) */ diff --git a/src/pj_math.cpp b/src/pj_math.cpp new file mode 100644 index 00000000..540ab9eb --- /dev/null +++ b/src/pj_math.cpp @@ -0,0 +1,108 @@ +/****************************************************************************** + * Project: PROJ + * Purpose: Make C99 math functions available on C89 systems + * Author: Kristian Evers + * + ****************************************************************************** + * Copyright (c) 2018, Kristian Evers + * + * 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 "proj_math.h" + +/* pj_isnan is used in gie.c which means that is has to */ +/* be exported in the Windows DLL and therefore needs */ +/* to be declared even though we have isnan() on the */ +/* system. */ + +#ifdef HAVE_C99_MATH +int pj_isnan (double x); +#endif + +/* Returns 0 if not a NaN and non-zero if val is a NaN */ +int pj_isnan (double x) { + /* cppcheck-suppress duplicateExpression */ + return x != x; +} + +#if !(defined(HAVE_C99_MATH) && HAVE_C99_MATH) + +/* Compute hypotenuse */ +double pj_hypot(double x, double y) { + x = fabs(x); + y = fabs(y); + if ( x < y ) { + x /= y; + return ( y * sqrt( 1. + x * x ) ); + } else { + y /= (x != 0.0 ? x : 1.0); + return ( x * sqrt( 1. + y * y ) ); + } +} + +/* Compute log(1+x) accurately */ +double pj_log1p(double x) { + volatile double + y = 1 + x, + z = y - 1; + /* Here's the explanation for this magic: y = 1 + z, exactly, and z + * approx x, thus log(y)/z (which is nearly constant near z = 0) returns + * a good approximation to the true log(1 + x)/x. The multiplication x * + * (log(y)/z) introduces little additional error. */ + return z == 0 ? x : x * log(y) / z; +} + +/* Compute asinh(x) accurately */ +double pj_asinh(double x) { + double y = fabs(x); /* Enforce odd parity */ + y = log1p(y * (1 + y/(hypot(1.0, y) + 1))); + return x > 0 ? y : (x < 0 ? -y : x); +} + +double pj_round(double x) { + /* The handling of corner cases is copied from boost; see + * https://github.com/boostorg/math/pull/8 + * with improvements to return -0.0 when appropriate */ + double t; + if (x == 0) + return x; /* Retain sign of 0 */ + else if (0 < x && x < 0.5) + return +0.0; + else if (0 > x && x > -0.5) + return -0.0; + else if (x > 0) { + t = ceil(x); + return 0.5 < t - x ? t - 1 : t; + } else { /* Includes NaN */ + t = floor(x); + return 0.5 < x - t ? t + 1 : t; + } +} + +long pj_lround(double x) { + /* Default value for overflow + NaN + (x == LONG_MIN) */ + long r = LONG_MIN; + x = round(x); + if (fabs(x) < -(double)LONG_MIN) /* Assume (double)LONG_MIN is exact */ + r = (int)x; + return r; +} + +#endif /* !(defined(HAVE_C99_MATH) && HAVE_C99_MATH) */ diff --git a/src/pj_mlfn.c b/src/pj_mlfn.c deleted file mode 100644 index 02e05c3a..00000000 --- a/src/pj_mlfn.c +++ /dev/null @@ -1,64 +0,0 @@ -#include - -#include "projects.h" - -/* meridional distance for ellipsoid and inverse -** 8th degree - accurate to < 1e-5 meters when used in conjunction -** with typical major axis values. -** Inverse determines phi to EPS (1e-11) radians, about 1e-6 seconds. -*/ -#define C00 1. -#define C02 .25 -#define C04 .046875 -#define C06 .01953125 -#define C08 .01068115234375 -#define C22 .75 -#define C44 .46875 -#define C46 .01302083333333333333 -#define C48 .00712076822916666666 -#define C66 .36458333333333333333 -#define C68 .00569661458333333333 -#define C88 .3076171875 -#define EPS 1e-11 -#define MAX_ITER 10 -#define EN_SIZE 5 - -double *pj_enfn(double es) { - double t, *en; - - en = (double *) pj_malloc(EN_SIZE * sizeof (double)); - if (0==en) - return 0; - - en[0] = C00 - es * (C02 + es * (C04 + es * (C06 + es * C08))); - en[1] = es * (C22 - es * (C04 + es * (C06 + es * C08))); - en[2] = (t = es * es) * (C44 - es * (C46 + es * C48)); - en[3] = (t *= es) * (C66 - es * C68); - en[4] = t * es * C88; - - return en; -} - - double -pj_mlfn(double phi, double sphi, double cphi, double *en) { - cphi *= sphi; - sphi *= sphi; - return(en[0] * phi - cphi * (en[1] + sphi*(en[2] - + sphi*(en[3] + sphi*en[4])))); -} - double -pj_inv_mlfn(projCtx ctx, double arg, double es, double *en) { - double s, t, phi, k = 1./(1.-es); - int i; - - phi = arg; - for (i = MAX_ITER; i ; --i) { /* rarely goes over 2 iterations */ - s = sin(phi); - t = 1. - es * s * s; - phi -= t = (pj_mlfn(phi, s, cos(phi), en) - arg) * (t * sqrt(t)) * k; - if (fabs(t) < EPS) - return phi; - } - pj_ctx_set_errno( ctx, PJD_ERR_NON_CONV_INV_MERI_DIST ); - return phi; -} diff --git a/src/pj_mlfn.cpp b/src/pj_mlfn.cpp new file mode 100644 index 00000000..02e05c3a --- /dev/null +++ b/src/pj_mlfn.cpp @@ -0,0 +1,64 @@ +#include + +#include "projects.h" + +/* meridional distance for ellipsoid and inverse +** 8th degree - accurate to < 1e-5 meters when used in conjunction +** with typical major axis values. +** Inverse determines phi to EPS (1e-11) radians, about 1e-6 seconds. +*/ +#define C00 1. +#define C02 .25 +#define C04 .046875 +#define C06 .01953125 +#define C08 .01068115234375 +#define C22 .75 +#define C44 .46875 +#define C46 .01302083333333333333 +#define C48 .00712076822916666666 +#define C66 .36458333333333333333 +#define C68 .00569661458333333333 +#define C88 .3076171875 +#define EPS 1e-11 +#define MAX_ITER 10 +#define EN_SIZE 5 + +double *pj_enfn(double es) { + double t, *en; + + en = (double *) pj_malloc(EN_SIZE * sizeof (double)); + if (0==en) + return 0; + + en[0] = C00 - es * (C02 + es * (C04 + es * (C06 + es * C08))); + en[1] = es * (C22 - es * (C04 + es * (C06 + es * C08))); + en[2] = (t = es * es) * (C44 - es * (C46 + es * C48)); + en[3] = (t *= es) * (C66 - es * C68); + en[4] = t * es * C88; + + return en; +} + + double +pj_mlfn(double phi, double sphi, double cphi, double *en) { + cphi *= sphi; + sphi *= sphi; + return(en[0] * phi - cphi * (en[1] + sphi*(en[2] + + sphi*(en[3] + sphi*en[4])))); +} + double +pj_inv_mlfn(projCtx ctx, double arg, double es, double *en) { + double s, t, phi, k = 1./(1.-es); + int i; + + phi = arg; + for (i = MAX_ITER; i ; --i) { /* rarely goes over 2 iterations */ + s = sin(phi); + t = 1. - es * s * s; + phi -= t = (pj_mlfn(phi, s, cos(phi), en) - arg) * (t * sqrt(t)) * k; + if (fabs(t) < EPS) + return phi; + } + pj_ctx_set_errno( ctx, PJD_ERR_NON_CONV_INV_MERI_DIST ); + return phi; +} diff --git a/src/pj_msfn.c b/src/pj_msfn.c deleted file mode 100644 index 999e73a7..00000000 --- a/src/pj_msfn.c +++ /dev/null @@ -1,7 +0,0 @@ -/* determine constant small m */ -#include -#include "projects.h" - double -pj_msfn(double sinphi, double cosphi, double es) { - return (cosphi / sqrt (1. - es * sinphi * sinphi)); -} diff --git a/src/pj_msfn.cpp b/src/pj_msfn.cpp new file mode 100644 index 00000000..999e73a7 --- /dev/null +++ b/src/pj_msfn.cpp @@ -0,0 +1,7 @@ +/* determine constant small m */ +#include +#include "projects.h" + double +pj_msfn(double sinphi, double cosphi, double es) { + return (cosphi / sqrt (1. - es * sinphi * sinphi)); +} diff --git a/src/pj_mutex.c b/src/pj_mutex.c deleted file mode 100644 index dc4a441b..00000000 --- a/src/pj_mutex.c +++ /dev/null @@ -1,261 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Mutex (thread lock) functions. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2009, Frank Warmerdam - * - * 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. - *****************************************************************************/ - - -/* projects.h and windows.h conflict - avoid this! */ - -#if defined(MUTEX_pthread) && !defined(_XOPEN_SOURCE) && !defined(__sun) -/* For pthread_mutexattr_settype */ -#define _XOPEN_SOURCE 500 -#endif - -/* For PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */ -#define _GNU_SOURCE - -#ifndef _WIN32 -#include "proj_config.h" -#include "projects.h" -#else -#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#endif -#include "proj_api.h" -#endif - -/* on win32 we always use win32 mutexes, even if pthreads are available */ -#if defined(_WIN32) && !defined(MUTEX_stub) -#ifndef MUTEX_win32 -# define MUTEX_win32 -#endif -# undef MUTEX_pthread -#endif - -#if !defined(MUTEX_stub) && !defined(MUTEX_pthread) && !defined(MUTEX_win32) -# define MUTEX_stub -#endif - -/************************************************************************/ -/* ==================================================================== */ -/* stub mutex implementation */ -/* ==================================================================== */ -/************************************************************************/ - -#ifdef MUTEX_stub - -/************************************************************************/ -/* pj_acquire_lock() */ -/* */ -/* Acquire the PROJ.4 lock. */ -/************************************************************************/ - -void pj_acquire_lock() -{ -} - -/************************************************************************/ -/* pj_release_lock() */ -/* */ -/* Release the PROJ.4 lock. */ -/************************************************************************/ - -void pj_release_lock() -{ -} - -/************************************************************************/ -/* pj_cleanup_lock() */ -/************************************************************************/ -void pj_cleanup_lock() -{ -} - -#endif /* def MUTEX_stub */ - -/************************************************************************/ -/* ==================================================================== */ -/* pthread mutex implementation */ -/* ==================================================================== */ -/************************************************************************/ - -#ifdef MUTEX_pthread - -#include "pthread.h" - -#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP -static pthread_mutex_t core_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; -#else -static pthread_mutex_t core_lock; - -/************************************************************************/ -/* pj_create_lock() */ -/************************************************************************/ - -static void pj_create_lock() -{ - /* - ** We need to ensure the core mutex is created in recursive mode - */ - pthread_mutexattr_t mutex_attr; - - pthread_mutexattr_init(&mutex_attr); -#ifdef HAVE_PTHREAD_MUTEX_RECURSIVE - pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); -#else - pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_NP); -#endif - pthread_mutex_init(&core_lock, &mutex_attr); -} - -#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */ - -/************************************************************************/ -/* pj_acquire_lock() */ -/* */ -/* Acquire the PROJ.4 lock. */ -/************************************************************************/ - -void pj_acquire_lock() -{ - -#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - static pthread_once_t sOnceKey = PTHREAD_ONCE_INIT; - if( pthread_once(&sOnceKey, pj_create_lock) != 0 ) - { - fprintf(stderr, "pthread_once() failed in pj_acquire_lock().\n"); - } -#endif - - pthread_mutex_lock( &core_lock); -} - -/************************************************************************/ -/* pj_release_lock() */ -/* */ -/* Release the PROJ.4 lock. */ -/************************************************************************/ - -void pj_release_lock() -{ - pthread_mutex_unlock( &core_lock ); -} - -/************************************************************************/ -/* pj_cleanup_lock() */ -/************************************************************************/ -void pj_cleanup_lock() -{ -} - -#endif /* def MUTEX_pthread */ - -/************************************************************************/ -/* ==================================================================== */ -/* win32 mutex implementation */ -/* ==================================================================== */ -/************************************************************************/ - -#ifdef MUTEX_win32 - -#include - -static HANDLE mutex_lock = NULL; - -#if _WIN32_WINNT >= 0x0600 - -/************************************************************************/ -/* pj_create_lock() */ -/************************************************************************/ - -static BOOL CALLBACK pj_create_lock(PINIT_ONCE InitOnce, - PVOID Parameter, - PVOID *Context) -{ - (void)InitOnce; - (void)Parameter; - (void)Context; - mutex_lock = CreateMutex( NULL, FALSE, NULL ); - return TRUE; -} -#endif - -/************************************************************************/ -/* pj_init_lock() */ -/************************************************************************/ - -static void pj_init_lock() - -{ -#if _WIN32_WINNT >= 0x0600 - static INIT_ONCE sInitOnce = INIT_ONCE_STATIC_INIT; - InitOnceExecuteOnce( &sInitOnce, pj_create_lock, NULL, NULL ); -#else - if( mutex_lock == NULL ) - mutex_lock = CreateMutex( NULL, FALSE, NULL ); -#endif -} - -/************************************************************************/ -/* pj_acquire_lock() */ -/* */ -/* Acquire the PROJ.4 lock. */ -/************************************************************************/ - -void pj_acquire_lock() -{ - if( mutex_lock == NULL ) - pj_init_lock(); - - WaitForSingleObject( mutex_lock, INFINITE ); -} - -/************************************************************************/ -/* pj_release_lock() */ -/* */ -/* Release the PROJ.4 lock. */ -/************************************************************************/ - -void pj_release_lock() -{ - if( mutex_lock == NULL ) - pj_init_lock(); - else - ReleaseMutex( mutex_lock ); -} - -/************************************************************************/ -/* pj_cleanup_lock() */ -/************************************************************************/ -void pj_cleanup_lock() -{ - if( mutex_lock != NULL ) - { - CloseHandle( mutex_lock ); - mutex_lock = NULL; - } -} - -#endif /* def MUTEX_win32 */ diff --git a/src/pj_mutex.cpp b/src/pj_mutex.cpp new file mode 100644 index 00000000..dc4a441b --- /dev/null +++ b/src/pj_mutex.cpp @@ -0,0 +1,261 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Mutex (thread lock) functions. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2009, Frank Warmerdam + * + * 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. + *****************************************************************************/ + + +/* projects.h and windows.h conflict - avoid this! */ + +#if defined(MUTEX_pthread) && !defined(_XOPEN_SOURCE) && !defined(__sun) +/* For pthread_mutexattr_settype */ +#define _XOPEN_SOURCE 500 +#endif + +/* For PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */ +#define _GNU_SOURCE + +#ifndef _WIN32 +#include "proj_config.h" +#include "projects.h" +#else +#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#endif +#include "proj_api.h" +#endif + +/* on win32 we always use win32 mutexes, even if pthreads are available */ +#if defined(_WIN32) && !defined(MUTEX_stub) +#ifndef MUTEX_win32 +# define MUTEX_win32 +#endif +# undef MUTEX_pthread +#endif + +#if !defined(MUTEX_stub) && !defined(MUTEX_pthread) && !defined(MUTEX_win32) +# define MUTEX_stub +#endif + +/************************************************************************/ +/* ==================================================================== */ +/* stub mutex implementation */ +/* ==================================================================== */ +/************************************************************************/ + +#ifdef MUTEX_stub + +/************************************************************************/ +/* pj_acquire_lock() */ +/* */ +/* Acquire the PROJ.4 lock. */ +/************************************************************************/ + +void pj_acquire_lock() +{ +} + +/************************************************************************/ +/* pj_release_lock() */ +/* */ +/* Release the PROJ.4 lock. */ +/************************************************************************/ + +void pj_release_lock() +{ +} + +/************************************************************************/ +/* pj_cleanup_lock() */ +/************************************************************************/ +void pj_cleanup_lock() +{ +} + +#endif /* def MUTEX_stub */ + +/************************************************************************/ +/* ==================================================================== */ +/* pthread mutex implementation */ +/* ==================================================================== */ +/************************************************************************/ + +#ifdef MUTEX_pthread + +#include "pthread.h" + +#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP +static pthread_mutex_t core_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; +#else +static pthread_mutex_t core_lock; + +/************************************************************************/ +/* pj_create_lock() */ +/************************************************************************/ + +static void pj_create_lock() +{ + /* + ** We need to ensure the core mutex is created in recursive mode + */ + pthread_mutexattr_t mutex_attr; + + pthread_mutexattr_init(&mutex_attr); +#ifdef HAVE_PTHREAD_MUTEX_RECURSIVE + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); +#else + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_NP); +#endif + pthread_mutex_init(&core_lock, &mutex_attr); +} + +#endif /* PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP */ + +/************************************************************************/ +/* pj_acquire_lock() */ +/* */ +/* Acquire the PROJ.4 lock. */ +/************************************************************************/ + +void pj_acquire_lock() +{ + +#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP + static pthread_once_t sOnceKey = PTHREAD_ONCE_INIT; + if( pthread_once(&sOnceKey, pj_create_lock) != 0 ) + { + fprintf(stderr, "pthread_once() failed in pj_acquire_lock().\n"); + } +#endif + + pthread_mutex_lock( &core_lock); +} + +/************************************************************************/ +/* pj_release_lock() */ +/* */ +/* Release the PROJ.4 lock. */ +/************************************************************************/ + +void pj_release_lock() +{ + pthread_mutex_unlock( &core_lock ); +} + +/************************************************************************/ +/* pj_cleanup_lock() */ +/************************************************************************/ +void pj_cleanup_lock() +{ +} + +#endif /* def MUTEX_pthread */ + +/************************************************************************/ +/* ==================================================================== */ +/* win32 mutex implementation */ +/* ==================================================================== */ +/************************************************************************/ + +#ifdef MUTEX_win32 + +#include + +static HANDLE mutex_lock = NULL; + +#if _WIN32_WINNT >= 0x0600 + +/************************************************************************/ +/* pj_create_lock() */ +/************************************************************************/ + +static BOOL CALLBACK pj_create_lock(PINIT_ONCE InitOnce, + PVOID Parameter, + PVOID *Context) +{ + (void)InitOnce; + (void)Parameter; + (void)Context; + mutex_lock = CreateMutex( NULL, FALSE, NULL ); + return TRUE; +} +#endif + +/************************************************************************/ +/* pj_init_lock() */ +/************************************************************************/ + +static void pj_init_lock() + +{ +#if _WIN32_WINNT >= 0x0600 + static INIT_ONCE sInitOnce = INIT_ONCE_STATIC_INIT; + InitOnceExecuteOnce( &sInitOnce, pj_create_lock, NULL, NULL ); +#else + if( mutex_lock == NULL ) + mutex_lock = CreateMutex( NULL, FALSE, NULL ); +#endif +} + +/************************************************************************/ +/* pj_acquire_lock() */ +/* */ +/* Acquire the PROJ.4 lock. */ +/************************************************************************/ + +void pj_acquire_lock() +{ + if( mutex_lock == NULL ) + pj_init_lock(); + + WaitForSingleObject( mutex_lock, INFINITE ); +} + +/************************************************************************/ +/* pj_release_lock() */ +/* */ +/* Release the PROJ.4 lock. */ +/************************************************************************/ + +void pj_release_lock() +{ + if( mutex_lock == NULL ) + pj_init_lock(); + else + ReleaseMutex( mutex_lock ); +} + +/************************************************************************/ +/* pj_cleanup_lock() */ +/************************************************************************/ +void pj_cleanup_lock() +{ + if( mutex_lock != NULL ) + { + CloseHandle( mutex_lock ); + mutex_lock = NULL; + } +} + +#endif /* def MUTEX_win32 */ diff --git a/src/pj_open_lib.c b/src/pj_open_lib.c deleted file mode 100644 index 6b908360..00000000 --- a/src/pj_open_lib.c +++ /dev/null @@ -1,247 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implementation of pj_open_lib(), and pj_set_finder(). These - * provide a standard interface for opening projections support - * data files. - * Author: Gerald Evenden, Frank Warmerdam - * - ****************************************************************************** - * Copyright (c) 1995, Gerald Evenden - * Copyright (c) 2002, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include -#include -#include -#include - -#include "proj_internal.h" -#include "projects.h" - -static const char *(*pj_finder)(const char *) = NULL; -static int path_count = 0; -static char **search_path = NULL; -static const char * proj_lib_name = -#ifdef PROJ_LIB -PROJ_LIB; -#else -0; -#endif - -/************************************************************************/ -/* pj_set_finder() */ -/************************************************************************/ - -void pj_set_finder( const char *(*new_finder)(const char *) ) - -{ - pj_finder = new_finder; -} - -/************************************************************************/ -/* pj_set_searchpath() */ -/* */ -/* Path control for callers that can't practically provide */ -/* pj_set_finder() style callbacks. Call with (0,NULL) as args */ -/* to clear the searchpath set. */ -/************************************************************************/ - -void pj_set_searchpath ( int count, const char **path ) -{ - int i; - - if (path_count > 0 && search_path != NULL) - { - for (i = 0; i < path_count; i++) - { - pj_dalloc(search_path[i]); - } - pj_dalloc(search_path); - path_count = 0; - search_path = NULL; - } - - if( count > 0 ) - { - search_path = pj_malloc(sizeof *search_path * count); - for (i = 0; i < count; i++) - { - search_path[i] = pj_malloc(strlen(path[i]) + 1); - strcpy(search_path[i], path[i]); - } - } - - path_count = count; -} - -/* just a couple of helper functions that lets other functions - access the otherwise private search path */ -const char * const *proj_get_searchpath(void) { - return (const char * const *)search_path; -} - -int proj_get_path_count(void) { - return path_count; -} -/************************************************************************/ -/* pj_open_lib_ex() */ -/************************************************************************/ - -static PAFile -pj_open_lib_ex(projCtx ctx, const char *name, const char *mode, - char* out_full_filename, size_t out_full_filename_size) { - char fname[MAX_PATH_FILENAME+1]; - const char *sysname; - PAFile fid; - int n = 0; - int i; -#ifdef WIN32 - static const char dir_chars[] = "/\\"; -#else - static const char dir_chars[] = "/"; -#endif - - if( out_full_filename != NULL && out_full_filename_size > 0 ) - out_full_filename[0] = '\0'; - - /* check if ~/name */ - if (*name == '~' && strchr(dir_chars,name[1]) ) - if ((sysname = getenv("HOME")) != NULL) { - if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) ) - { - return NULL; - } - (void)strcpy(fname, sysname); - fname[n = (int)strlen(fname)] = DIR_CHAR; - fname[++n] = '\0'; - (void)strcpy(fname+n, name + 1); - sysname = fname; - } else - return NULL; - - /* or fixed path: /name, ./name or ../name */ - else if (strchr(dir_chars,*name) - || (*name == '.' && strchr(dir_chars,name[1])) - || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2])) - || (name[1] == ':' && strchr(dir_chars,name[2])) ) - sysname = name; - - /* or try to use application provided file finder */ - else if( pj_finder != NULL && pj_finder( name ) != NULL ) - sysname = pj_finder( name ); - - /* or is environment PROJ_LIB defined */ - else if ((sysname = getenv("PROJ_LIB")) || (sysname = proj_lib_name)) { - if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) ) - { - return NULL; - } - (void)strcpy(fname, sysname); - fname[n = (int)strlen(fname)] = DIR_CHAR; - fname[++n] = '\0'; - (void)strcpy(fname+n, name); - sysname = fname; - } else /* just try it bare bones */ - sysname = name; - - if ((fid = pj_ctx_fopen(ctx, sysname, mode)) != NULL) - { - if( out_full_filename != NULL && out_full_filename_size > 0 ) - { - strncpy(out_full_filename, sysname, out_full_filename_size); - out_full_filename[out_full_filename_size-1] = '\0'; - } - errno = 0; - } - - /* If none of those work and we have a search path, try it */ - if (!fid && path_count > 0) - { - for (i = 0; fid == NULL && i < path_count; i++) - { - if( strlen(search_path[i]) + 1 + strlen(name) + 1 <= sizeof(fname) ) - { - sprintf(fname, "%s%c%s", search_path[i], DIR_CHAR, name); - sysname = fname; - fid = pj_ctx_fopen(ctx, sysname, mode); - } - } - if (fid) - { - if( out_full_filename != NULL && out_full_filename_size > 0 ) - { - strncpy(out_full_filename, sysname, out_full_filename_size); - out_full_filename[out_full_filename_size-1] = '\0'; - } - errno = 0; - } - } - - if( ctx->last_errno == 0 && errno != 0 ) - pj_ctx_set_errno( ctx, errno ); - - pj_log( ctx, PJ_LOG_DEBUG_MAJOR, - "pj_open_lib(%s): call fopen(%s) - %s", - name, sysname, - fid == NULL ? "failed" : "succeeded" ); - - return(fid); -} - -/************************************************************************/ -/* pj_open_lib() */ -/************************************************************************/ - -PAFile -pj_open_lib(projCtx ctx, const char *name, const char *mode) { - return pj_open_lib_ex(ctx, name, mode, NULL, 0); -} - -/************************************************************************/ -/* pj_find_file() */ -/************************************************************************/ - -/** Returns the full filename corresponding to a proj resource file specified - * as a short filename. - * - * @param ctx context. - * @param short_filename short filename (e.g. egm96_15.gtx) - * @param out_full_filename output buffer, of size out_full_filename_size, that - * will receive the full filename on success. - * Will be zero-terminated. - * @param out_full_filename_size size of out_full_filename. - * @return 1 if the file was found, 0 otherwise. - */ -int pj_find_file(projCtx ctx, const char *short_filename, - char* out_full_filename, size_t out_full_filename_size) -{ - PAFile f = pj_open_lib_ex(ctx, short_filename, "rb", out_full_filename, - out_full_filename_size); - if( f != NULL ) - { - pj_ctx_fclose(ctx, f); - return 1; - } - return 0; -} diff --git a/src/pj_open_lib.cpp b/src/pj_open_lib.cpp new file mode 100644 index 00000000..31a2c2e1 --- /dev/null +++ b/src/pj_open_lib.cpp @@ -0,0 +1,247 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implementation of pj_open_lib(), and pj_set_finder(). These + * provide a standard interface for opening projections support + * data files. + * Author: Gerald Evenden, Frank Warmerdam + * + ****************************************************************************** + * Copyright (c) 1995, Gerald Evenden + * Copyright (c) 2002, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include +#include +#include +#include + +#include "proj_internal.h" +#include "projects.h" + +static const char *(*pj_finder)(const char *) = NULL; +static int path_count = 0; +static char **search_path = NULL; +static const char * proj_lib_name = +#ifdef PROJ_LIB +PROJ_LIB; +#else +0; +#endif + +/************************************************************************/ +/* pj_set_finder() */ +/************************************************************************/ + +void pj_set_finder( const char *(*new_finder)(const char *) ) + +{ + pj_finder = new_finder; +} + +/************************************************************************/ +/* pj_set_searchpath() */ +/* */ +/* Path control for callers that can't practically provide */ +/* pj_set_finder() style callbacks. Call with (0,NULL) as args */ +/* to clear the searchpath set. */ +/************************************************************************/ + +void pj_set_searchpath ( int count, const char **path ) +{ + int i; + + if (path_count > 0 && search_path != NULL) + { + for (i = 0; i < path_count; i++) + { + pj_dalloc(search_path[i]); + } + pj_dalloc(search_path); + path_count = 0; + search_path = NULL; + } + + if( count > 0 ) + { + search_path = static_cast(pj_malloc(sizeof *search_path * count)); + for (i = 0; i < count; i++) + { + search_path[i] = static_cast(pj_malloc(strlen(path[i]) + 1)); + strcpy(search_path[i], path[i]); + } + } + + path_count = count; +} + +/* just a couple of helper functions that lets other functions + access the otherwise private search path */ +const char * const *proj_get_searchpath(void) { + return (const char * const *)search_path; +} + +int proj_get_path_count(void) { + return path_count; +} +/************************************************************************/ +/* pj_open_lib_ex() */ +/************************************************************************/ + +static PAFile +pj_open_lib_ex(projCtx ctx, const char *name, const char *mode, + char* out_full_filename, size_t out_full_filename_size) { + char fname[MAX_PATH_FILENAME+1]; + const char *sysname; + PAFile fid; + int n = 0; + int i; +#ifdef WIN32 + static const char dir_chars[] = "/\\"; +#else + static const char dir_chars[] = "/"; +#endif + + if( out_full_filename != NULL && out_full_filename_size > 0 ) + out_full_filename[0] = '\0'; + + /* check if ~/name */ + if (*name == '~' && strchr(dir_chars,name[1]) ) + if ((sysname = getenv("HOME")) != NULL) { + if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) ) + { + return NULL; + } + (void)strcpy(fname, sysname); + fname[n = (int)strlen(fname)] = DIR_CHAR; + fname[++n] = '\0'; + (void)strcpy(fname+n, name + 1); + sysname = fname; + } else + return NULL; + + /* or fixed path: /name, ./name or ../name */ + else if (strchr(dir_chars,*name) + || (*name == '.' && strchr(dir_chars,name[1])) + || (!strncmp(name, "..", 2) && strchr(dir_chars,name[2])) + || (name[1] == ':' && strchr(dir_chars,name[2])) ) + sysname = name; + + /* or try to use application provided file finder */ + else if( pj_finder != NULL && pj_finder( name ) != NULL ) + sysname = pj_finder( name ); + + /* or is environment PROJ_LIB defined */ + else if ((sysname = getenv("PROJ_LIB")) || (sysname = proj_lib_name)) { + if( strlen(sysname) + 1 + strlen(name) + 1 > sizeof(fname) ) + { + return NULL; + } + (void)strcpy(fname, sysname); + fname[n = (int)strlen(fname)] = DIR_CHAR; + fname[++n] = '\0'; + (void)strcpy(fname+n, name); + sysname = fname; + } else /* just try it bare bones */ + sysname = name; + + if ((fid = pj_ctx_fopen(ctx, sysname, mode)) != NULL) + { + if( out_full_filename != NULL && out_full_filename_size > 0 ) + { + strncpy(out_full_filename, sysname, out_full_filename_size); + out_full_filename[out_full_filename_size-1] = '\0'; + } + errno = 0; + } + + /* If none of those work and we have a search path, try it */ + if (!fid && path_count > 0) + { + for (i = 0; fid == NULL && i < path_count; i++) + { + if( strlen(search_path[i]) + 1 + strlen(name) + 1 <= sizeof(fname) ) + { + sprintf(fname, "%s%c%s", search_path[i], DIR_CHAR, name); + sysname = fname; + fid = pj_ctx_fopen(ctx, sysname, mode); + } + } + if (fid) + { + if( out_full_filename != NULL && out_full_filename_size > 0 ) + { + strncpy(out_full_filename, sysname, out_full_filename_size); + out_full_filename[out_full_filename_size-1] = '\0'; + } + errno = 0; + } + } + + if( ctx->last_errno == 0 && errno != 0 ) + pj_ctx_set_errno( ctx, errno ); + + pj_log( ctx, PJ_LOG_DEBUG_MAJOR, + "pj_open_lib(%s): call fopen(%s) - %s", + name, sysname, + fid == NULL ? "failed" : "succeeded" ); + + return(fid); +} + +/************************************************************************/ +/* pj_open_lib() */ +/************************************************************************/ + +PAFile +pj_open_lib(projCtx ctx, const char *name, const char *mode) { + return pj_open_lib_ex(ctx, name, mode, NULL, 0); +} + +/************************************************************************/ +/* pj_find_file() */ +/************************************************************************/ + +/** Returns the full filename corresponding to a proj resource file specified + * as a short filename. + * + * @param ctx context. + * @param short_filename short filename (e.g. egm96_15.gtx) + * @param out_full_filename output buffer, of size out_full_filename_size, that + * will receive the full filename on success. + * Will be zero-terminated. + * @param out_full_filename_size size of out_full_filename. + * @return 1 if the file was found, 0 otherwise. + */ +int pj_find_file(projCtx ctx, const char *short_filename, + char* out_full_filename, size_t out_full_filename_size) +{ + PAFile f = pj_open_lib_ex(ctx, short_filename, "rb", out_full_filename, + out_full_filename_size); + if( f != NULL ) + { + pj_ctx_fclose(ctx, f); + return 1; + } + return 0; +} diff --git a/src/pj_param.c b/src/pj_param.c deleted file mode 100644 index 4078dc83..00000000 --- a/src/pj_param.c +++ /dev/null @@ -1,189 +0,0 @@ -/* put parameters in linked list and retrieve */ - -#include -#include -#include -#include -#include - -#include "projects.h" - -/* create parameter list entry */ -paralist *pj_mkparam(const char *str) { - paralist *newitem; - - if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) { - newitem->used = 0; - newitem->next = 0; - if (*str == '+') - ++str; - (void)strcpy(newitem->param, str); - } - return newitem; -} - - -/* As pj_mkparam, but payload ends at first whitespace, rather than at end of */ -paralist *pj_mkparam_ws (const char *str) { - paralist *newitem; - size_t len = 0; - - if (0==str) - return 0; - - /* Find start and length of string */ - while (isspace (*str)) - str++; - while ((!isspace(str[len])) && 0!=str[len]) - len++; - if (*str == '+') { - str++; - len--; - } - - /* Use calloc to automagically 0-terminate the copy */ - newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len + 1); - if (0==newitem) - return 0; - memmove(newitem->param, str, len); - - newitem->used = 0; - newitem->next = 0; - - return newitem; -} - -/**************************************************************************************/ -paralist *pj_param_exists (paralist *list, const char *parameter) { -/*************************************************************************************** - Determine whether a given parameter exists in a paralist. If it does, return - a pointer to the corresponding list element - otherwise return 0. - - In support of the pipeline syntax, the search is terminated once a "+step" list - element is reached, in which case a 0 is returned, unless the parameter - searched for is actually "step", in which case a pointer to the "step" list - element is returned. - - This function is equivalent to the pj_param (...) call with the "opt" argument - set to the parameter name preceeeded by a 't'. But by using this one, one avoids - writing the code allocating memory for a new copy of parameter name, and prepending - the t (for compile time known names, this is obviously not an issue). -***************************************************************************************/ - paralist *next = list; - char *c = strchr (parameter, '='); - size_t len = strlen (parameter); - if (c) - len = c - parameter; - if (list==0) - return 0; - - for (next = list; next; next = next->next) { - if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) { - next->used = 1; - return next; - } - if (0==strcmp (parameter, "step")) - return 0; - } - - return 0; -} - - -/************************************************************************/ -/* pj_param() */ -/* */ -/* Test for presence or get parameter value. The first */ -/* character in `opt' is a parameter type which can take the */ -/* values: */ -/* */ -/* `t' - test for presence, return TRUE/FALSE in PROJVALUE.i */ -/* `i' - integer value returned in PROJVALUE.i */ -/* `d' - simple valued real input returned in PROJVALUE.f */ -/* `r' - degrees (DMS translation applied), returned as */ -/* radians in PROJVALUE.f */ -/* `s' - string returned in PROJVALUE.s */ -/* `b' - test for t/T/f/F, return in PROJVALUE.i */ -/* */ -/* Search is terminated when "step" is found, in which case */ -/* 0 is returned, unless "step" was the target searched for. */ -/* */ -/************************************************************************/ - -PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) { - - int type; - unsigned l; - PROJVALUE value = {0}; - - if ( ctx == NULL ) - ctx = pj_get_default_ctx(); - - type = *opt++; - - if (0==strchr ("tbirds", type)) { - fprintf(stderr, "invalid request to pj_param, fatal\n"); - exit(1); - } - - pl = pj_param_exists (pl, opt); - if (type == 't') { - value.i = pl != 0; - return value; - } - - /* Not found */ - if (0==pl) { - /* Return value after the switch, so that the return path is */ - /* taken in all cases */ - switch (type) { - case 'b': case 'i': - value.i = 0; - break; - case 'd': case 'r': - value.f = 0.; - break; - case 's': - value.s = 0; - break; - } - return value; - } - - /* Found parameter - now find its value */ - pl->used |= 1; - l = (int) strlen(opt); - opt = pl->param + l; - if (*opt == '=') - ++opt; - - switch (type) { - case 'i': /* integer input */ - value.i = atoi(opt); - break; - case 'd': /* simple real input */ - value.f = pj_atof(opt); - break; - case 'r': /* degrees input */ - value.f = dmstor_ctx(ctx, opt, 0); - break; - case 's': /* char string */ - value.s = (char *) opt; - break; - case 'b': /* boolean */ - switch (*opt) { - case 'F': case 'f': - value.i = 0; - break; - case '\0': case 'T': case 't': - value.i = 1; - break; - default: - pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM); - value.i = 0; - break; - } - break; - } - return value; -} diff --git a/src/pj_param.cpp b/src/pj_param.cpp new file mode 100644 index 00000000..1887afe9 --- /dev/null +++ b/src/pj_param.cpp @@ -0,0 +1,189 @@ +/* put parameters in linked list and retrieve */ + +#include +#include +#include +#include +#include + +#include "projects.h" + +/* create parameter list entry */ +paralist *pj_mkparam(const char *str) { + paralist *newitem; + + if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != NULL) { + newitem->used = 0; + newitem->next = 0; + if (*str == '+') + ++str; + (void)strcpy(newitem->param, str); + } + return newitem; +} + + +/* As pj_mkparam, but payload ends at first whitespace, rather than at end of */ +paralist *pj_mkparam_ws (const char *str) { + paralist *newitem; + size_t len = 0; + + if (0==str) + return 0; + + /* Find start and length of string */ + while (isspace (*str)) + str++; + while ((!isspace(str[len])) && 0!=str[len]) + len++; + if (*str == '+') { + str++; + len--; + } + + /* Use calloc to automagically 0-terminate the copy */ + newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len + 1); + if (0==newitem) + return 0; + memmove(newitem->param, str, len); + + newitem->used = 0; + newitem->next = 0; + + return newitem; +} + +/**************************************************************************************/ +paralist *pj_param_exists (paralist *list, const char *parameter) { +/*************************************************************************************** + Determine whether a given parameter exists in a paralist. If it does, return + a pointer to the corresponding list element - otherwise return 0. + + In support of the pipeline syntax, the search is terminated once a "+step" list + element is reached, in which case a 0 is returned, unless the parameter + searched for is actually "step", in which case a pointer to the "step" list + element is returned. + + This function is equivalent to the pj_param (...) call with the "opt" argument + set to the parameter name preceeeded by a 't'. But by using this one, one avoids + writing the code allocating memory for a new copy of parameter name, and prepending + the t (for compile time known names, this is obviously not an issue). +***************************************************************************************/ + paralist *next = list; + const char *c = strchr (parameter, '='); + size_t len = strlen (parameter); + if (c) + len = c - parameter; + if (list==0) + return 0; + + for (next = list; next; next = next->next) { + if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) { + next->used = 1; + return next; + } + if (0==strcmp (parameter, "step")) + return 0; + } + + return 0; +} + + +/************************************************************************/ +/* pj_param() */ +/* */ +/* Test for presence or get parameter value. The first */ +/* character in `opt' is a parameter type which can take the */ +/* values: */ +/* */ +/* `t' - test for presence, return TRUE/FALSE in PROJVALUE.i */ +/* `i' - integer value returned in PROJVALUE.i */ +/* `d' - simple valued real input returned in PROJVALUE.f */ +/* `r' - degrees (DMS translation applied), returned as */ +/* radians in PROJVALUE.f */ +/* `s' - string returned in PROJVALUE.s */ +/* `b' - test for t/T/f/F, return in PROJVALUE.i */ +/* */ +/* Search is terminated when "step" is found, in which case */ +/* 0 is returned, unless "step" was the target searched for. */ +/* */ +/************************************************************************/ + +PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) { + + int type; + unsigned l; + PROJVALUE value = {0}; + + if ( ctx == NULL ) + ctx = pj_get_default_ctx(); + + type = *opt++; + + if (0==strchr ("tbirds", type)) { + fprintf(stderr, "invalid request to pj_param, fatal\n"); + exit(1); + } + + pl = pj_param_exists (pl, opt); + if (type == 't') { + value.i = pl != 0; + return value; + } + + /* Not found */ + if (0==pl) { + /* Return value after the switch, so that the return path is */ + /* taken in all cases */ + switch (type) { + case 'b': case 'i': + value.i = 0; + break; + case 'd': case 'r': + value.f = 0.; + break; + case 's': + value.s = 0; + break; + } + return value; + } + + /* Found parameter - now find its value */ + pl->used |= 1; + l = (int) strlen(opt); + opt = pl->param + l; + if (*opt == '=') + ++opt; + + switch (type) { + case 'i': /* integer input */ + value.i = atoi(opt); + break; + case 'd': /* simple real input */ + value.f = pj_atof(opt); + break; + case 'r': /* degrees input */ + value.f = dmstor_ctx(ctx, opt, 0); + break; + case 's': /* char string */ + value.s = (char *) opt; + break; + case 'b': /* boolean */ + switch (*opt) { + case 'F': case 'f': + value.i = 0; + break; + case '\0': case 'T': case 't': + value.i = 1; + break; + default: + pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM); + value.i = 0; + break; + } + break; + } + return value; +} diff --git a/src/pj_phi2.c b/src/pj_phi2.c deleted file mode 100644 index a83302e6..00000000 --- a/src/pj_phi2.c +++ /dev/null @@ -1,46 +0,0 @@ -/* Determine latitude angle phi-2. */ - -#include - -#include "projects.h" - -static const double TOL = 1.0e-10; -static const int N_ITER = 15; - -/*****************************************************************************/ -double pj_phi2(projCtx ctx, double ts, double e) { -/****************************************************************************** -Determine latitude angle phi-2. -Inputs: - ts = exp(-psi) where psi is the isometric latitude (dimensionless) - e = eccentricity of the ellipsoid (dimensionless) -Output: - phi = geographic latitude (radians) -Here isometric latitude is defined by - psi = log( tan(pi/4 + phi/2) * - ( (1 - e*sin(phi)) / (1 + e*sin(phi)) )^(e/2) ) - = asinh(tan(phi)) - e * atanh(e * sin(phi)) -This routine inverts this relation using the iterative scheme given -by Snyder (1987), Eqs. (7-9) - (7-11) -*******************************************************************************/ - double eccnth = .5 * e; - double Phi = M_HALFPI - 2. * atan(ts); - double con; - int i = N_ITER; - - for(;;) { - double dphi; - con = e * sin(Phi); - dphi = M_HALFPI - 2. * atan(ts * pow((1. - con) / - (1. + con), eccnth)) - Phi; - - Phi += dphi; - - if (fabs(dphi) > TOL && --i) - continue; - break; - } - if (i <= 0) - pj_ctx_set_errno(ctx, PJD_ERR_NON_CON_INV_PHI2); - return Phi; -} diff --git a/src/pj_phi2.cpp b/src/pj_phi2.cpp new file mode 100644 index 00000000..a83302e6 --- /dev/null +++ b/src/pj_phi2.cpp @@ -0,0 +1,46 @@ +/* Determine latitude angle phi-2. */ + +#include + +#include "projects.h" + +static const double TOL = 1.0e-10; +static const int N_ITER = 15; + +/*****************************************************************************/ +double pj_phi2(projCtx ctx, double ts, double e) { +/****************************************************************************** +Determine latitude angle phi-2. +Inputs: + ts = exp(-psi) where psi is the isometric latitude (dimensionless) + e = eccentricity of the ellipsoid (dimensionless) +Output: + phi = geographic latitude (radians) +Here isometric latitude is defined by + psi = log( tan(pi/4 + phi/2) * + ( (1 - e*sin(phi)) / (1 + e*sin(phi)) )^(e/2) ) + = asinh(tan(phi)) - e * atanh(e * sin(phi)) +This routine inverts this relation using the iterative scheme given +by Snyder (1987), Eqs. (7-9) - (7-11) +*******************************************************************************/ + double eccnth = .5 * e; + double Phi = M_HALFPI - 2. * atan(ts); + double con; + int i = N_ITER; + + for(;;) { + double dphi; + con = e * sin(Phi); + dphi = M_HALFPI - 2. * atan(ts * pow((1. - con) / + (1. + con), eccnth)) - Phi; + + Phi += dphi; + + if (fabs(dphi) > TOL && --i) + continue; + break; + } + if (i <= 0) + pj_ctx_set_errno(ctx, PJD_ERR_NON_CON_INV_PHI2); + return Phi; +} diff --git a/src/pj_pr_list.c b/src/pj_pr_list.c deleted file mode 100644 index 4e71e471..00000000 --- a/src/pj_pr_list.c +++ /dev/null @@ -1,104 +0,0 @@ -/* print projection's list of parameters */ - -#include -#include -#include - -#include "projects.h" - -#define LINE_LEN 72 - static int -pr_list(PJ *P, int not_used) { - paralist *t; - int l, n = 1, flag = 0; - - (void)putchar('#'); - for (t = P->params; t; t = t->next) - if ((!not_used && t->used) || (not_used && !t->used)) { - l = (int)strlen(t->param) + 1; - if (n + l > LINE_LEN) { - (void)fputs("\n#", stdout); - n = 2; - } - (void)putchar(' '); - if (*(t->param) != '+') - (void)putchar('+'); - (void)fputs(t->param, stdout); - n += l; - } else - flag = 1; - if (n > 1) - (void)putchar('\n'); - return flag; -} - void /* print link list of projection parameters */ -pj_pr_list(PJ *P) { - char const *s; - - (void)putchar('#'); - for (s = P->descr; *s ; ++s) { - (void)putchar(*s); - if (*s == '\n') - (void)putchar('#'); - } - (void)putchar('\n'); - if (pr_list(P, 0)) { - (void)fputs("#--- following specified but NOT used\n", stdout); - (void)pr_list(P, 1); - } -} - -/************************************************************************/ -/* pj_get_def() */ -/* */ -/* Returns the PROJ.4 command string that would produce this */ -/* definition expanded as much as possible. For instance, */ -/* +init= calls and +datum= definitions would be expanded. */ -/************************************************************************/ - -char *pj_get_def( PJ *P, int options ) - -{ - paralist *t; - int l; - char *definition; - size_t def_max = 10; - (void) options; - - definition = (char *) pj_malloc(def_max); - if (!definition) - return NULL; - definition[0] = '\0'; - - for (t = P->params; t; t = t->next) - { - /* skip unused parameters ... mostly appended defaults and stuff */ - if (!t->used) - continue; - - /* grow the resulting string if needed */ - l = (int)strlen(t->param) + 1; - if( strlen(definition) + l + 5 > def_max ) - { - char *def2; - - def_max = def_max * 2 + l + 5; - def2 = (char *) pj_malloc(def_max); - if (def2) { - strcpy( def2, definition ); - pj_dalloc( definition ); - definition = def2; - } - else { - pj_dalloc( definition ); - return NULL; - } - } - - /* append this parameter */ - strcat( definition, " +" ); - strcat( definition, t->param ); - } - - return definition; -} diff --git a/src/pj_pr_list.cpp b/src/pj_pr_list.cpp new file mode 100644 index 00000000..4e71e471 --- /dev/null +++ b/src/pj_pr_list.cpp @@ -0,0 +1,104 @@ +/* print projection's list of parameters */ + +#include +#include +#include + +#include "projects.h" + +#define LINE_LEN 72 + static int +pr_list(PJ *P, int not_used) { + paralist *t; + int l, n = 1, flag = 0; + + (void)putchar('#'); + for (t = P->params; t; t = t->next) + if ((!not_used && t->used) || (not_used && !t->used)) { + l = (int)strlen(t->param) + 1; + if (n + l > LINE_LEN) { + (void)fputs("\n#", stdout); + n = 2; + } + (void)putchar(' '); + if (*(t->param) != '+') + (void)putchar('+'); + (void)fputs(t->param, stdout); + n += l; + } else + flag = 1; + if (n > 1) + (void)putchar('\n'); + return flag; +} + void /* print link list of projection parameters */ +pj_pr_list(PJ *P) { + char const *s; + + (void)putchar('#'); + for (s = P->descr; *s ; ++s) { + (void)putchar(*s); + if (*s == '\n') + (void)putchar('#'); + } + (void)putchar('\n'); + if (pr_list(P, 0)) { + (void)fputs("#--- following specified but NOT used\n", stdout); + (void)pr_list(P, 1); + } +} + +/************************************************************************/ +/* pj_get_def() */ +/* */ +/* Returns the PROJ.4 command string that would produce this */ +/* definition expanded as much as possible. For instance, */ +/* +init= calls and +datum= definitions would be expanded. */ +/************************************************************************/ + +char *pj_get_def( PJ *P, int options ) + +{ + paralist *t; + int l; + char *definition; + size_t def_max = 10; + (void) options; + + definition = (char *) pj_malloc(def_max); + if (!definition) + return NULL; + definition[0] = '\0'; + + for (t = P->params; t; t = t->next) + { + /* skip unused parameters ... mostly appended defaults and stuff */ + if (!t->used) + continue; + + /* grow the resulting string if needed */ + l = (int)strlen(t->param) + 1; + if( strlen(definition) + l + 5 > def_max ) + { + char *def2; + + def_max = def_max * 2 + l + 5; + def2 = (char *) pj_malloc(def_max); + if (def2) { + strcpy( def2, definition ); + pj_dalloc( definition ); + definition = def2; + } + else { + pj_dalloc( definition ); + return NULL; + } + } + + /* append this parameter */ + strcat( definition, " +" ); + strcat( definition, t->param ); + } + + return definition; +} diff --git a/src/pj_qsfn.c b/src/pj_qsfn.c deleted file mode 100644 index c18a7b95..00000000 --- a/src/pj_qsfn.c +++ /dev/null @@ -1,22 +0,0 @@ -/* determine small q */ -#include -#include "projects.h" - -# define EPSILON 1.0e-7 - -double pj_qsfn(double sinphi, double e, double one_es) { - double con, div1, div2; - - if (e >= EPSILON) { - con = e * sinphi; - div1 = 1.0 - con * con; - div2 = 1.0 + con; - - /* avoid zero division, fail gracefully */ - if (div1 == 0.0 || div2 == 0.0) - return HUGE_VAL; - - return (one_es * (sinphi / div1 - (.5 / e) * log ((1. - con) / div2 ))); - } else - return (sinphi + sinphi); -} diff --git a/src/pj_qsfn.cpp b/src/pj_qsfn.cpp new file mode 100644 index 00000000..c18a7b95 --- /dev/null +++ b/src/pj_qsfn.cpp @@ -0,0 +1,22 @@ +/* determine small q */ +#include +#include "projects.h" + +# define EPSILON 1.0e-7 + +double pj_qsfn(double sinphi, double e, double one_es) { + double con, div1, div2; + + if (e >= EPSILON) { + con = e * sinphi; + div1 = 1.0 - con * con; + div2 = 1.0 + con; + + /* avoid zero division, fail gracefully */ + if (div1 == 0.0 || div2 == 0.0) + return HUGE_VAL; + + return (one_es * (sinphi / div1 - (.5 / e) * log ((1. - con) / div2 ))); + } else + return (sinphi + sinphi); +} diff --git a/src/pj_release.c b/src/pj_release.c deleted file mode 100644 index 9beb45ef..00000000 --- a/src/pj_release.c +++ /dev/null @@ -1,18 +0,0 @@ -/* <<< Release Notice for library >>> */ - -#include "proj.h" -#include "projects.h" - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -char const pj_release[] = - "Rel. " - STR(PROJ_VERSION_MAJOR)"." - STR(PROJ_VERSION_MINOR)"." - STR(PROJ_VERSION_PATCH)", " - "March 1st, 2019"; - -const char *pj_get_release() { - return pj_release; -} diff --git a/src/pj_release.cpp b/src/pj_release.cpp new file mode 100644 index 00000000..9beb45ef --- /dev/null +++ b/src/pj_release.cpp @@ -0,0 +1,18 @@ +/* <<< Release Notice for library >>> */ + +#include "proj.h" +#include "projects.h" + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + +char const pj_release[] = + "Rel. " + STR(PROJ_VERSION_MAJOR)"." + STR(PROJ_VERSION_MINOR)"." + STR(PROJ_VERSION_PATCH)", " + "March 1st, 2019"; + +const char *pj_get_release() { + return pj_release; +} diff --git a/src/pj_strerrno.c b/src/pj_strerrno.c deleted file mode 100644 index 16042f79..00000000 --- a/src/pj_strerrno.c +++ /dev/null @@ -1,109 +0,0 @@ -/* list of projection system pj_errno values */ - -#include -#include -#include - -#include "proj.h" -#include "projects.h" - -static const char * const -pj_err_list[] = { - "no arguments in initialization list", /* -1 */ - "no options found in 'init' file", /* -2 */ - "no colon in init= string", /* -3 */ - "projection not named", /* -4 */ - "unknown projection id", /* -5 */ - "effective eccentricity = 1.", /* -6 */ - "unknown unit conversion id", /* -7 */ - "invalid boolean param argument", /* -8 */ - "unknown elliptical parameter name", /* -9 */ - "reciprocal flattening (1/f) = 0", /* -10 */ - "|radius reference latitude| > 90", /* -11 */ - "squared eccentricity < 0", /* -12 */ - "major axis or radius = 0 or not given", /* -13 */ - "latitude or longitude exceeded limits", /* -14 */ - "invalid x or y", /* -15 */ - "improperly formed DMS value", /* -16 */ - "non-convergent inverse meridional dist", /* -17 */ - "non-convergent inverse phi2", /* -18 */ - "acos/asin: |arg| >1.+1e-14", /* -19 */ - "tolerance condition error", /* -20 */ - "conic lat_1 = -lat_2", /* -21 */ - "lat_1 >= 90", /* -22 */ - "lat_1 = 0", /* -23 */ - "lat_ts >= 90", /* -24 */ - "no distance between control points", /* -25 */ - "projection not selected to be rotated", /* -26 */ - "W <= 0 or M <= 0", /* -27 */ - "lsat not in 1-5 range", /* -28 */ - "path not in range", /* -29 */ - "h <= 0", /* -30 */ - "k <= 0", /* -31 */ - "lat_0 = 0 or 90 or alpha = 90", /* -32 */ - "lat_1=lat_2 or lat_1=0 or lat_2=90", /* -33 */ - "elliptical usage required", /* -34 */ - "invalid UTM zone number", /* -35 */ - "arg(s) out of range for Tcheby eval", /* -36 */ - "failed to find projection to be rotated", /* -37 */ - "failed to load datum shift file", /* -38 */ - "both n & m must be spec'd and > 0", /* -39 */ - "n <= 0, n > 1 or not specified", /* -40 */ - "lat_1 or lat_2 not specified", /* -41 */ - "|lat_1| == |lat_2|", /* -42 */ - "lat_0 is pi/2 from mean lat", /* -43 */ - "unparseable coordinate system definition", /* -44 */ - "geocentric transformation missing z or ellps", /* -45 */ - "unknown prime meridian conversion id", /* -46 */ - "illegal axis orientation combination", /* -47 */ - "point not within available datum shift grids", /* -48 */ - "invalid sweep axis, choose x or y", /* -49 */ - "malformed pipeline", /* -50 */ - "unit conversion factor must be > 0", /* -51 */ - "invalid scale", /* -52 */ - "non-convergent computation", /* -53 */ - "missing required arguments", /* -54 */ - "lat_0 = 0", /* -55 */ - "ellipsoidal usage unsupported", /* -56 */ - "only one +init allowed for non-pipeline operations", /* -57 */ - "argument not numerical or out of range", /* -58 */ - "inconsistent unit type between input and output", /* -59 */ - - /* When adding error messages, remember to update ID defines in - projects.h, and transient_error array in pj_transform */ -}; - -char *pj_strerrno(int err) { - const int max_error = 9999; - static char note[50]; - size_t adjusted_err; - - if (0==err) - return 0; - - /* System error codes are positive */ - if (err > 0) { -#ifdef HAVE_STRERROR - return strerror(err); -#else - /* Defend string boundary against exorbitantly large err values */ - /* which may occur on platforms with 64-bit ints */ - sprintf(note, "no system list, errno: %d\n", - (err < max_error) ? err: max_error); - return note; -#endif - } - - /* PROJ.4 error codes are negative: -1 to -9999 */ - adjusted_err = err < -max_error ? max_error : -err - 1; - if (adjusted_err < (sizeof(pj_err_list) / sizeof(char *))) - return (char *)pj_err_list[adjusted_err]; - - sprintf(note, "invalid projection system error (%d)", - (err > -max_error) ? err: -max_error); - return note; -} - -const char* proj_errno_string(int err) { - return pj_strerrno(err); -} diff --git a/src/pj_strerrno.cpp b/src/pj_strerrno.cpp new file mode 100644 index 00000000..16042f79 --- /dev/null +++ b/src/pj_strerrno.cpp @@ -0,0 +1,109 @@ +/* list of projection system pj_errno values */ + +#include +#include +#include + +#include "proj.h" +#include "projects.h" + +static const char * const +pj_err_list[] = { + "no arguments in initialization list", /* -1 */ + "no options found in 'init' file", /* -2 */ + "no colon in init= string", /* -3 */ + "projection not named", /* -4 */ + "unknown projection id", /* -5 */ + "effective eccentricity = 1.", /* -6 */ + "unknown unit conversion id", /* -7 */ + "invalid boolean param argument", /* -8 */ + "unknown elliptical parameter name", /* -9 */ + "reciprocal flattening (1/f) = 0", /* -10 */ + "|radius reference latitude| > 90", /* -11 */ + "squared eccentricity < 0", /* -12 */ + "major axis or radius = 0 or not given", /* -13 */ + "latitude or longitude exceeded limits", /* -14 */ + "invalid x or y", /* -15 */ + "improperly formed DMS value", /* -16 */ + "non-convergent inverse meridional dist", /* -17 */ + "non-convergent inverse phi2", /* -18 */ + "acos/asin: |arg| >1.+1e-14", /* -19 */ + "tolerance condition error", /* -20 */ + "conic lat_1 = -lat_2", /* -21 */ + "lat_1 >= 90", /* -22 */ + "lat_1 = 0", /* -23 */ + "lat_ts >= 90", /* -24 */ + "no distance between control points", /* -25 */ + "projection not selected to be rotated", /* -26 */ + "W <= 0 or M <= 0", /* -27 */ + "lsat not in 1-5 range", /* -28 */ + "path not in range", /* -29 */ + "h <= 0", /* -30 */ + "k <= 0", /* -31 */ + "lat_0 = 0 or 90 or alpha = 90", /* -32 */ + "lat_1=lat_2 or lat_1=0 or lat_2=90", /* -33 */ + "elliptical usage required", /* -34 */ + "invalid UTM zone number", /* -35 */ + "arg(s) out of range for Tcheby eval", /* -36 */ + "failed to find projection to be rotated", /* -37 */ + "failed to load datum shift file", /* -38 */ + "both n & m must be spec'd and > 0", /* -39 */ + "n <= 0, n > 1 or not specified", /* -40 */ + "lat_1 or lat_2 not specified", /* -41 */ + "|lat_1| == |lat_2|", /* -42 */ + "lat_0 is pi/2 from mean lat", /* -43 */ + "unparseable coordinate system definition", /* -44 */ + "geocentric transformation missing z or ellps", /* -45 */ + "unknown prime meridian conversion id", /* -46 */ + "illegal axis orientation combination", /* -47 */ + "point not within available datum shift grids", /* -48 */ + "invalid sweep axis, choose x or y", /* -49 */ + "malformed pipeline", /* -50 */ + "unit conversion factor must be > 0", /* -51 */ + "invalid scale", /* -52 */ + "non-convergent computation", /* -53 */ + "missing required arguments", /* -54 */ + "lat_0 = 0", /* -55 */ + "ellipsoidal usage unsupported", /* -56 */ + "only one +init allowed for non-pipeline operations", /* -57 */ + "argument not numerical or out of range", /* -58 */ + "inconsistent unit type between input and output", /* -59 */ + + /* When adding error messages, remember to update ID defines in + projects.h, and transient_error array in pj_transform */ +}; + +char *pj_strerrno(int err) { + const int max_error = 9999; + static char note[50]; + size_t adjusted_err; + + if (0==err) + return 0; + + /* System error codes are positive */ + if (err > 0) { +#ifdef HAVE_STRERROR + return strerror(err); +#else + /* Defend string boundary against exorbitantly large err values */ + /* which may occur on platforms with 64-bit ints */ + sprintf(note, "no system list, errno: %d\n", + (err < max_error) ? err: max_error); + return note; +#endif + } + + /* PROJ.4 error codes are negative: -1 to -9999 */ + adjusted_err = err < -max_error ? max_error : -err - 1; + if (adjusted_err < (sizeof(pj_err_list) / sizeof(char *))) + return (char *)pj_err_list[adjusted_err]; + + sprintf(note, "invalid projection system error (%d)", + (err > -max_error) ? err: -max_error); + return note; +} + +const char* proj_errno_string(int err) { + return pj_strerrno(err); +} diff --git a/src/pj_strtod.c b/src/pj_strtod.c deleted file mode 100644 index f604a013..00000000 --- a/src/pj_strtod.c +++ /dev/null @@ -1,195 +0,0 @@ -/****************************************************************************** - * - * Derived from GDAL port/cpl_strtod.cpp - * Purpose: Functions to convert ASCII string to floating point number. - * Author: Andrey Kiselev, dron@ak4719.spb.edu. - * - ****************************************************************************** - * Copyright (c) 2006, Andrey Kiselev - * Copyright (c) 2008-2012, Even Rouault - * - * 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 -#include -#include -#include - -#include "projects.h" - -/* Windows nmake build doesn't have a proj_config.h, but HAVE_LOCALECONV */ -/* is defined in the compilation line */ -#ifndef HAVE_LOCALECONV -#include "proj_config.h" -#endif - -#define PJ_STRTOD_WORK_BUFFER_SIZE 64 - -/************************************************************************/ -/* pj_atof() */ -/************************************************************************/ - -/** - * Converts ASCII string to floating point number. - * - * This function converts the initial portion of the string pointed to - * by nptr to double floating point representation. The behaviour is the - * same as - * - * pj_strtod(nptr, (char **)NULL); - * - * This function does the same as standard atof(3), but does not take - * locale in account. That means, the decimal delimiter is always '.' - * (decimal point). - * - * @param nptr Pointer to string to convert. - * - * @return Converted value. - */ -double pj_atof( const char* nptr ) -{ - return pj_strtod(nptr, NULL); -} - - -/************************************************************************/ -/* replace_point_by_locale_point() */ -/************************************************************************/ - -static char* replace_point_by_locale_point(const char* pszNumber, char point, - char* pszWorkBuffer) -{ -#if !defined(HAVE_LOCALECONV) - -#if defined(_MSC_VER) /* Visual C++ */ -#pragma message("localeconv not available") -#else -#warning "localeconv not available" -#endif - - static char byPoint = 0; - if (byPoint == 0) - { - char szBuf[16]; - sprintf(szBuf, "%.1f", 1.0); - byPoint = szBuf[1]; - } - if (point != byPoint) - { - const char* pszPoint = strchr(pszNumber, point); - if (pszPoint) - { - char* pszNew; - if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE ) - { - strcpy(pszWorkBuffer, pszNumber); - pszNew = pszWorkBuffer; - } - else { - pszNew = pj_strdup(pszNumber); - if (!pszNew) - return NULL; - } - pszNew[pszPoint - pszNumber] = byPoint; - return pszNew; - } - } -#else - struct lconv *poLconv = localeconv(); - if ( poLconv - && poLconv->decimal_point - && poLconv->decimal_point[0] != '\0' ) - { - char byPoint = poLconv->decimal_point[0]; - - if (point != byPoint) - { - const char* pszLocalePoint = strchr(pszNumber, byPoint); - const char* pszPoint = strchr(pszNumber, point); - if (pszPoint || pszLocalePoint) - { - char* pszNew; - if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE ) - { - strcpy(pszWorkBuffer, pszNumber); - pszNew = pszWorkBuffer; - } - else { - pszNew = pj_strdup(pszNumber); - if (!pszNew) - return NULL; - } - if( pszLocalePoint ) - pszNew[pszLocalePoint - pszNumber] = ' '; - if( pszPoint ) - pszNew[pszPoint - pszNumber] = byPoint; - return pszNew; - } - } - } -#endif - return (char*) pszNumber; -} - -/************************************************************************/ -/* pj_strtod() */ -/************************************************************************/ - -/** - * Converts ASCII string to floating point number. - * - * This function converts the initial portion of the string pointed to - * by nptr to double floating point representation. This function does the - * same as standard strtod(3), but does not take locale in account and use - * decimal point. - * - * @param nptr Pointer to string to convert. - * @param endptr If is not NULL, a pointer to the character after the last - * character used in the conversion is stored in the location referenced - * by endptr. - * - * @return Converted value. - */ -double pj_strtod( const char *nptr, char **endptr ) -{ -/* -------------------------------------------------------------------- */ -/* We are implementing a simple method here: copy the input string */ -/* into the temporary buffer, replace the specified decimal delimiter */ -/* with the one, taken from locale settings and use standard strtod() */ -/* on that buffer. */ -/* -------------------------------------------------------------------- */ - double dfValue; - int nError; - char szWorkBuffer[PJ_STRTOD_WORK_BUFFER_SIZE]; - - char* pszNumber = replace_point_by_locale_point(nptr, '.', szWorkBuffer); - - dfValue = strtod( pszNumber, endptr ); - nError = errno; - - if ( endptr ) - *endptr = (char *)nptr + (*endptr - pszNumber); - - if (pszNumber != (char*) nptr && pszNumber != szWorkBuffer ) - free( pszNumber ); - - errno = nError; - return dfValue; -} diff --git a/src/pj_strtod.cpp b/src/pj_strtod.cpp new file mode 100644 index 00000000..f604a013 --- /dev/null +++ b/src/pj_strtod.cpp @@ -0,0 +1,195 @@ +/****************************************************************************** + * + * Derived from GDAL port/cpl_strtod.cpp + * Purpose: Functions to convert ASCII string to floating point number. + * Author: Andrey Kiselev, dron@ak4719.spb.edu. + * + ****************************************************************************** + * Copyright (c) 2006, Andrey Kiselev + * Copyright (c) 2008-2012, Even Rouault + * + * 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 +#include +#include +#include + +#include "projects.h" + +/* Windows nmake build doesn't have a proj_config.h, but HAVE_LOCALECONV */ +/* is defined in the compilation line */ +#ifndef HAVE_LOCALECONV +#include "proj_config.h" +#endif + +#define PJ_STRTOD_WORK_BUFFER_SIZE 64 + +/************************************************************************/ +/* pj_atof() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. The behaviour is the + * same as + * + * pj_strtod(nptr, (char **)NULL); + * + * This function does the same as standard atof(3), but does not take + * locale in account. That means, the decimal delimiter is always '.' + * (decimal point). + * + * @param nptr Pointer to string to convert. + * + * @return Converted value. + */ +double pj_atof( const char* nptr ) +{ + return pj_strtod(nptr, NULL); +} + + +/************************************************************************/ +/* replace_point_by_locale_point() */ +/************************************************************************/ + +static char* replace_point_by_locale_point(const char* pszNumber, char point, + char* pszWorkBuffer) +{ +#if !defined(HAVE_LOCALECONV) + +#if defined(_MSC_VER) /* Visual C++ */ +#pragma message("localeconv not available") +#else +#warning "localeconv not available" +#endif + + static char byPoint = 0; + if (byPoint == 0) + { + char szBuf[16]; + sprintf(szBuf, "%.1f", 1.0); + byPoint = szBuf[1]; + } + if (point != byPoint) + { + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint) + { + char* pszNew; + if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE ) + { + strcpy(pszWorkBuffer, pszNumber); + pszNew = pszWorkBuffer; + } + else { + pszNew = pj_strdup(pszNumber); + if (!pszNew) + return NULL; + } + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; + } + } +#else + struct lconv *poLconv = localeconv(); + if ( poLconv + && poLconv->decimal_point + && poLconv->decimal_point[0] != '\0' ) + { + char byPoint = poLconv->decimal_point[0]; + + if (point != byPoint) + { + const char* pszLocalePoint = strchr(pszNumber, byPoint); + const char* pszPoint = strchr(pszNumber, point); + if (pszPoint || pszLocalePoint) + { + char* pszNew; + if( strlen(pszNumber) < PJ_STRTOD_WORK_BUFFER_SIZE ) + { + strcpy(pszWorkBuffer, pszNumber); + pszNew = pszWorkBuffer; + } + else { + pszNew = pj_strdup(pszNumber); + if (!pszNew) + return NULL; + } + if( pszLocalePoint ) + pszNew[pszLocalePoint - pszNumber] = ' '; + if( pszPoint ) + pszNew[pszPoint - pszNumber] = byPoint; + return pszNew; + } + } + } +#endif + return (char*) pszNumber; +} + +/************************************************************************/ +/* pj_strtod() */ +/************************************************************************/ + +/** + * Converts ASCII string to floating point number. + * + * This function converts the initial portion of the string pointed to + * by nptr to double floating point representation. This function does the + * same as standard strtod(3), but does not take locale in account and use + * decimal point. + * + * @param nptr Pointer to string to convert. + * @param endptr If is not NULL, a pointer to the character after the last + * character used in the conversion is stored in the location referenced + * by endptr. + * + * @return Converted value. + */ +double pj_strtod( const char *nptr, char **endptr ) +{ +/* -------------------------------------------------------------------- */ +/* We are implementing a simple method here: copy the input string */ +/* into the temporary buffer, replace the specified decimal delimiter */ +/* with the one, taken from locale settings and use standard strtod() */ +/* on that buffer. */ +/* -------------------------------------------------------------------- */ + double dfValue; + int nError; + char szWorkBuffer[PJ_STRTOD_WORK_BUFFER_SIZE]; + + char* pszNumber = replace_point_by_locale_point(nptr, '.', szWorkBuffer); + + dfValue = strtod( pszNumber, endptr ); + nError = errno; + + if ( endptr ) + *endptr = (char *)nptr + (*endptr - pszNumber); + + if (pszNumber != (char*) nptr && pszNumber != szWorkBuffer ) + free( pszNumber ); + + errno = nError; + return dfValue; +} diff --git a/src/pj_transform.c b/src/pj_transform.c deleted file mode 100644 index 6982676e..00000000 --- a/src/pj_transform.c +++ /dev/null @@ -1,1060 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Perform overall coordinate system to coordinate system - * transformations (pj_transform() function) including reprojection - * and datum shifting. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2000, Frank Warmerdam - * - * 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 -#include -#include - -#include "projects.h" -#include "geocent.h" - - -/* Apply transformation to observation - in forward or inverse direction */ -/* Copied from proj.h */ -enum PJ_DIRECTION { - PJ_FWD = 1, /* Forward */ - PJ_IDENT = 0, /* Do nothing */ - PJ_INV = -1 /* Inverse */ -}; -typedef enum PJ_DIRECTION PJ_DIRECTION; - -/* Copied from proj.h FIXME */ -int proj_errno_reset (const PJ *P); - - -static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, - long point_count, int point_offset, - double *x, double *y, double *z ); - -#ifndef SRS_WGS84_SEMIMAJOR -#define SRS_WGS84_SEMIMAJOR 6378137.0 -#endif - -#ifndef SRS_WGS84_ESQUARED -#define SRS_WGS84_ESQUARED 0.0066943799901413165 -#endif - -#define Dx_BF (defn->datum_params[0]) -#define Dy_BF (defn->datum_params[1]) -#define Dz_BF (defn->datum_params[2]) -#define Rx_BF (defn->datum_params[3]) -#define Ry_BF (defn->datum_params[4]) -#define Rz_BF (defn->datum_params[5]) -#define M_BF (defn->datum_params[6]) - -/* -** This table is intended to indicate for any given error code -** whether that error will occur for all locations (ie. -** it is a problem with the coordinate system as a whole) in which case the -** value would be 0, or if the problem is with the point being transformed -** in which case the value is 1. -** -** At some point we might want to move this array in with the error message -** list or something, but while experimenting with it this should be fine. -** -** -** NOTE (2017-10-01): Non-transient errors really should have resulted in a -** PJ==0 during initialization, and hence should be handled at the level -** before calling pj_transform. The only obvious example of the contrary -** appears to be the PJD_ERR_GRID_AREA case, which may also be taken to -** mean "no grids available" -** -** -*/ - -static const int transient_error[60] = { - /* 0 1 2 3 4 5 6 7 8 9 */ - /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, - /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, - /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - /* 50 to 59 */ 1, 0, 1, 0, 1, 1, 1, 1, 0, 0 }; - - -/* -------------------------------------------------------------------- */ -/* Read transient_error[] in a safe way. */ -/* -------------------------------------------------------------------- */ -static int get_transient_error_value(int pos_index) -{ - const int array_size = - (int)(sizeof(transient_error) / sizeof(transient_error[0])); - if( pos_index < 0 || pos_index >= array_size ) { - return 0; - } - return transient_error[pos_index]; -} - - -/* -------------------------------------------------------------------- */ -/* Transform unusual input coordinate axis orientation to */ -/* standard form if needed. */ -/* -------------------------------------------------------------------- */ -static int adjust_axes (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { - /* Nothing to do? */ - if (0==strcmp(P->axis,"enu")) - return 0; - - return adjust_axis( P->ctx, P->axis, - dir==PJ_FWD ? 1: 0, n, dist, x, y, z ); -} - - - -/* ----------------------------------------------------------------------- */ -/* Transform geographic (lat/long) source coordinates to */ -/* cartesian ("geocentric"), if needed */ -/* ----------------------------------------------------------------------- */ -static int geographic_to_cartesian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { - int res; - long i; - double fac = P->to_meter; - - /* Nothing to do? */ - if (!P->is_geocent) - return 0; - - if ( z == NULL ) { - pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); - return PJD_ERR_GEOCENTRIC; - } - - if (PJ_FWD==dir) { - fac = P->fr_meter; - res = pj_geodetic_to_geocentric( P->a_orig, P->es_orig, n, dist, x, y, z ); - if (res) - return res; - } - - if (fac != 1.0) { - for( i = 0; i < n; i++ ) { - if( x[dist*i] != HUGE_VAL ) { - x[dist*i] *= fac; - y[dist*i] *= fac; - z[dist*i] *= fac; - } - } - } - - if (PJ_FWD==dir) - return 0; - return pj_geocentric_to_geodetic( - P->a_orig, P->es_orig, - n, dist, - x, y, z - ); -} - - - - - - - - - - -/* -------------------------------------------------------------------- */ -/* Transform destination points to projection coordinates, if */ -/* desired. */ -/* */ -/* Ought to fold this into projected_to_geographic */ -/* -------------------------------------------------------------------- */ -static int geographic_to_projected (PJ *P, long n, int dist, double *x, double *y, double *z) { - long i; - - /* Nothing to do? */ - if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) - return 0; - if (P->is_geocent) - return 0; - - if(P->fwd3d != NULL && !(z == NULL && P->is_latlong)) - { - /* Three dimensions must be defined */ - if ( z == NULL) - { - pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); - return PJD_ERR_GEOCENTRIC; - } - - for( i = 0; i < n; i++ ) - { - XYZ projected_loc; - LPZ geodetic_loc; - - geodetic_loc.u = x[dist*i]; - geodetic_loc.v = y[dist*i]; - geodetic_loc.w = z[dist*i]; - - if (geodetic_loc.u == HUGE_VAL) - continue; - - proj_errno_reset( P ); - projected_loc = pj_fwd3d( geodetic_loc, P); - if( P->ctx->last_errno != 0 ) - { - if( (P->ctx->last_errno != EDOM - && P->ctx->last_errno != ERANGE) - && (P->ctx->last_errno > 0 - || P->ctx->last_errno < -44 || n == 1 - || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) - { - return P->ctx->last_errno; - } - else - { - projected_loc.u = HUGE_VAL; - projected_loc.v = HUGE_VAL; - projected_loc.w = HUGE_VAL; - } - } - - x[dist*i] = projected_loc.u; - y[dist*i] = projected_loc.v; - z[dist*i] = projected_loc.w; - } - return 0; - } - - for( i = 0; i ctx->last_errno != 0 ) - { - if( (P->ctx->last_errno != EDOM - && P->ctx->last_errno != ERANGE) - && (P->ctx->last_errno > 0 - || P->ctx->last_errno < -44 || n == 1 - || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) - { - return P->ctx->last_errno; - } - else - { - projected_loc.u = HUGE_VAL; - projected_loc.v = HUGE_VAL; - } - } - - x[dist*i] = projected_loc.u; - y[dist*i] = projected_loc.v; - } - return 0; -} - - - - - -/* ----------------------------------------------------------------------- */ -/* Transform projected source coordinates to lat/long, if needed */ -/* ----------------------------------------------------------------------- */ -static int projected_to_geographic (PJ *P, long n, int dist, double *x, double *y, double *z) { - long i; - - /* Nothing to do? */ - if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) - return 0; - if (P->is_geocent) - return 0; - - /* Check first if projection is invertible. */ - if( (P->inv3d == NULL) && (P->inv == NULL)) - { - pj_ctx_set_errno(pj_get_ctx(P), PJD_ERR_NON_CONV_INV_MERI_DIST); - pj_log( pj_get_ctx(P), PJ_LOG_ERROR, - "pj_transform(): source projection not invertable" ); - return PJD_ERR_NON_CONV_INV_MERI_DIST; - } - - /* If invertible - First try inv3d if defined */ - if (P->inv3d != NULL && !(z == NULL && P->is_latlong)) - { - /* Three dimensions must be defined */ - if ( z == NULL) - { - pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); - return PJD_ERR_GEOCENTRIC; - } - - for (i=0; i < n; i++) - { - XYZ projected_loc; - XYZ geodetic_loc; - - projected_loc.u = x[dist*i]; - projected_loc.v = y[dist*i]; - projected_loc.w = z[dist*i]; - - if (projected_loc.u == HUGE_VAL) - continue; - - proj_errno_reset( P ); - geodetic_loc = pj_inv3d(projected_loc, P); - if( P->ctx->last_errno != 0 ) - { - if( (P->ctx->last_errno != EDOM - && P->ctx->last_errno != ERANGE) - && (P->ctx->last_errno > 0 - || P->ctx->last_errno < -44 || n == 1 - || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) - { - return P->ctx->last_errno; - } - else - { - geodetic_loc.u = HUGE_VAL; - geodetic_loc.v = HUGE_VAL; - geodetic_loc.w = HUGE_VAL; - } - } - - x[dist*i] = geodetic_loc.u; - y[dist*i] = geodetic_loc.v; - z[dist*i] = geodetic_loc.w; - - } - return 0; - } - - /* Fallback to the original PROJ.4 API 2d inversion - inv */ - for( i = 0; i < n; i++ ) { - XY projected_loc; - LP geodetic_loc; - - projected_loc.u = x[dist*i]; - projected_loc.v = y[dist*i]; - - if( projected_loc.u == HUGE_VAL ) - continue; - - proj_errno_reset( P ); - geodetic_loc = pj_inv( projected_loc, P ); - if( P->ctx->last_errno != 0 ) - { - if( (P->ctx->last_errno != EDOM - && P->ctx->last_errno != ERANGE) - && (P->ctx->last_errno > 0 - || P->ctx->last_errno < -44 || n == 1 - || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) - { - return P->ctx->last_errno; - } - else - { - geodetic_loc.u = HUGE_VAL; - geodetic_loc.v = HUGE_VAL; - } - } - - x[dist*i] = geodetic_loc.u; - y[dist*i] = geodetic_loc.v; - } - return 0; -} - - - -/* -------------------------------------------------------------------- */ -/* Adjust for the prime meridian if needed. */ -/* -------------------------------------------------------------------- */ -static int prime_meridian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x) { - int i; - double pm = P->from_greenwich; - - /* Nothing to do? */ - if (pm==0.0) - return 0; - if (!(P->is_geocent || P->is_latlong)) - return 0; - - if (dir==PJ_FWD) - pm = -pm; - - for (i = 0; i < n; i++) - if (x[dist*i] != HUGE_VAL) - x[dist*i] += pm; - - return 0; -} - - - -/* -------------------------------------------------------------------- */ -/* Adjust for vertical scale factor if needed */ -/* -------------------------------------------------------------------- */ -static int height_unit (PJ *P, PJ_DIRECTION dir, long n, int dist, double *z) { - int i; - double fac = P->vto_meter; - - if (PJ_FWD==dir) - fac = P->vfr_meter; - - /* Nothing to do? */ - if (fac==1.0) - return 0; - if (0==z) - return 0; - if (P->is_latlong) - return 0; /* done in pj_inv3d() / pj_fwd3d() */ - - for (i = 0; i < n; i++) - if (z[dist*i] != HUGE_VAL ) - z[dist*i] *= fac; - - return 0; -} - - - -/* -------------------------------------------------------------------- */ -/* Transform to ellipsoidal heights if needed */ -/* -------------------------------------------------------------------- */ -static int geometric_to_orthometric (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { - int err; - if (0==P->has_geoid_vgrids) - return 0; - if (z==0) - return PJD_ERR_GEOCENTRIC; - err = pj_apply_vgridshift (P, "sgeoidgrids", - &(P->vgridlist_geoid), - &(P->vgridlist_geoid_count), - dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); - if (err) - return pj_ctx_get_errno(P->ctx); - return 0; -} - - - -/* -------------------------------------------------------------------- */ -/* Convert datums if needed, and possible. */ -/* -------------------------------------------------------------------- */ -static int datum_transform (PJ *P, PJ *Q, long n, int dist, double *x, double *y, double *z) { - if (0==pj_datum_transform (P, Q, n, dist, x, y, z)) - return 0; - if (P->ctx->last_errno) - return P->ctx->last_errno; - return Q->ctx->last_errno; -} - - - - - -/* -------------------------------------------------------------------- */ -/* If a wrapping center other than 0 is provided, rewrap around */ -/* the suggested center (for latlong coordinate systems only). */ -/* -------------------------------------------------------------------- */ -static int long_wrap (PJ *P, long n, int dist, double *x) { - long i; - - /* Nothing to do? */ - if (P->is_geocent) - return 0; - if (!P->is_long_wrap_set) - return 0; - if (!P->is_latlong) - return 0; - - for (i = 0; i < n; i++ ) { - double val = x[dist*i]; - if (val == HUGE_VAL) - continue; - - /* Get fast in ] -2 PI, 2 PI [ range */ - val = fmod(val, M_TWOPI); - while( val < P->long_wrap_center - M_PI ) - val += M_TWOPI; - while( val > P->long_wrap_center + M_PI ) - val -= M_TWOPI; - x[dist*i] = val; - } - return 0; -} - - - -/************************************************************************/ -/* pj_transform() */ -/* */ -/* Currently this function doesn't recognise if two projections */ -/* are identical (to short circuit reprojection) because it is */ -/* difficult to compare PJ structures (since there are some */ -/* projection specific components). */ -/************************************************************************/ - -int pj_transform( - PJ *src, PJ *dst, - long point_count, int point_offset, - double *x, double *y, double *z -){ - int err; - - src->ctx->last_errno = 0; - dst->ctx->last_errno = 0; - - if( point_offset == 0 ) - point_offset = 1; - - /* Bring input to "normal form": longitude, latitude, ellipsoidal height */ - - err = adjust_axes (src, PJ_INV, point_count, point_offset, x, y, z); - if (err) - return err; - err = geographic_to_cartesian (src, PJ_INV, point_count, point_offset, x, y, z); - if (err) - return err; - err = projected_to_geographic (src, point_count, point_offset, x, y, z); - if (err) - return err; - err = prime_meridian (src, PJ_INV, point_count, point_offset, x); - if (err) - return err; - err = height_unit (src, PJ_INV, point_count, point_offset, z); - if (err) - return err; - err = geometric_to_orthometric (src, PJ_INV, point_count, point_offset, x, y, z); - if (err) - return err; - - /* At the center of the process we do the datum shift (if needed) */ - - err = datum_transform(src, dst, point_count, point_offset, x, y, z ); - if (err) - return err; - - /* Now get out on the other side: Bring "normal form" to output form */ - - err = geometric_to_orthometric (dst, PJ_FWD, point_count, point_offset, x, y, z); - if (err) - return err; - err = height_unit (dst, PJ_FWD, point_count, point_offset, z); - if (err) - return err; - err = prime_meridian (dst, PJ_FWD, point_count, point_offset, x); - if (err) - return err; - err = geographic_to_cartesian (dst, PJ_FWD, point_count, point_offset, x, y, z); - if (err) - return err; - err = geographic_to_projected (dst, point_count, point_offset, x, y, z); - if (err) - return err; - err = long_wrap (dst, point_count, point_offset, x); - if (err) - return err; - err = adjust_axes (dst, PJ_FWD, point_count, point_offset, x, y, z); - if (err) - return err; - - return 0; -} - - - -/************************************************************************/ -/* pj_geodetic_to_geocentric() */ -/************************************************************************/ - -int pj_geodetic_to_geocentric( double a, double es, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - double b; - int i; - GeocentricInfo gi; - int ret_errno = 0; - - if( es == 0.0 ) - b = a; - else - b = a * sqrt(1-es); - - if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) - { - return PJD_ERR_GEOCENTRIC; - } - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - - if( x[io] == HUGE_VAL ) - continue; - - if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], - x+io, y+io, z+io ) != 0 ) - { - ret_errno = PJD_ERR_LAT_OR_LON_EXCEED_LIMIT; - x[io] = y[io] = HUGE_VAL; - /* but keep processing points! */ - } - } - - return ret_errno; -} - -/************************************************************************/ -/* pj_geocentric_to_geodetic() */ -/************************************************************************/ - -int pj_geocentric_to_geodetic( double a, double es, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - double b; - int i; - GeocentricInfo gi; - - if( es == 0.0 ) - b = a; - else - b = a * sqrt(1-es); - - if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) - { - return PJD_ERR_GEOCENTRIC; - } - - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - - if( x[io] == HUGE_VAL ) - continue; - - pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], - y+io, x+io, z+io ); - } - - return 0; -} - -/************************************************************************/ -/* pj_compare_datums() */ -/* */ -/* Returns TRUE if the two datums are identical, otherwise */ -/* FALSE. */ -/************************************************************************/ - -int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) - -{ - if( srcdefn->datum_type != dstdefn->datum_type ) - { - return 0; - } - else if( srcdefn->a_orig != dstdefn->a_orig - || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 ) - { - /* the tolerance for es is to ensure that GRS80 and WGS84 are - considered identical */ - return 0; - } - else if( srcdefn->datum_type == PJD_3PARAM ) - { - return (srcdefn->datum_params[0] == dstdefn->datum_params[0] - && srcdefn->datum_params[1] == dstdefn->datum_params[1] - && srcdefn->datum_params[2] == dstdefn->datum_params[2]); - } - else if( srcdefn->datum_type == PJD_7PARAM ) - { - return (srcdefn->datum_params[0] == dstdefn->datum_params[0] - && srcdefn->datum_params[1] == dstdefn->datum_params[1] - && srcdefn->datum_params[2] == dstdefn->datum_params[2] - && srcdefn->datum_params[3] == dstdefn->datum_params[3] - && srcdefn->datum_params[4] == dstdefn->datum_params[4] - && srcdefn->datum_params[5] == dstdefn->datum_params[5] - && srcdefn->datum_params[6] == dstdefn->datum_params[6]); - } - else if( srcdefn->datum_type == PJD_GRIDSHIFT ) - { - const char* srcnadgrids = - pj_param(srcdefn->ctx, srcdefn->params,"snadgrids").s; - const char* dstnadgrids = - pj_param(dstdefn->ctx, dstdefn->params,"snadgrids").s; - return srcnadgrids != 0 && dstnadgrids != 0 && - strcmp( srcnadgrids, dstnadgrids ) == 0; - } - else - return 1; -} - -/************************************************************************/ -/* pj_geocentic_to_wgs84() */ -/************************************************************************/ - -static -int pj_geocentric_to_wgs84( PJ *defn, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - - if( defn->datum_type == PJD_3PARAM ) - { - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - - if( x[io] == HUGE_VAL ) - continue; - - x[io] = x[io] + Dx_BF; - y[io] = y[io] + Dy_BF; - z[io] = z[io] + Dz_BF; - } - } - else if( defn->datum_type == PJD_7PARAM ) - { - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - double x_out, y_out, z_out; - - if( x[io] == HUGE_VAL ) - continue; - - x_out = M_BF*( x[io] - Rz_BF*y[io] + Ry_BF*z[io]) + Dx_BF; - y_out = M_BF*( Rz_BF*x[io] + y[io] - Rx_BF*z[io]) + Dy_BF; - z_out = M_BF*(-Ry_BF*x[io] + Rx_BF*y[io] + z[io]) + Dz_BF; - - x[io] = x_out; - y[io] = y_out; - z[io] = z_out; - } - } - - return 0; -} - -/************************************************************************/ -/* pj_geocentic_from_wgs84() */ -/************************************************************************/ - -static -int pj_geocentric_from_wgs84( PJ *defn, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - int i; - - if( defn->datum_type == PJD_3PARAM ) - { - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - - if( x[io] == HUGE_VAL ) - continue; - - x[io] = x[io] - Dx_BF; - y[io] = y[io] - Dy_BF; - z[io] = z[io] - Dz_BF; - } - } - else if( defn->datum_type == PJD_7PARAM ) - { - for( i = 0; i < point_count; i++ ) - { - long io = i * point_offset; - double x_tmp, y_tmp, z_tmp; - - if( x[io] == HUGE_VAL ) - continue; - - x_tmp = (x[io] - Dx_BF) / M_BF; - y_tmp = (y[io] - Dy_BF) / M_BF; - z_tmp = (z[io] - Dz_BF) / M_BF; - - x[io] = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp; - y[io] = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp; - z[io] = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp; - } - } - - return 0; -} - -/************************************************************************/ -/* pj_datum_transform() */ -/* */ -/* The input should be long/lat/z coordinates in radians in the */ -/* source datum, and the output should be long/lat/z */ -/* coordinates in radians in the destination datum. */ -/************************************************************************/ - -int pj_datum_transform( PJ *src, PJ *dst, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - double src_a, src_es, dst_a, dst_es; - int z_is_temp = FALSE; - -/* -------------------------------------------------------------------- */ -/* We cannot do any meaningful datum transformation if either */ -/* the source or destination are of an unknown datum type */ -/* (ie. only a +ellps declaration, no +datum). This is new */ -/* behavior for PROJ 4.6.0. */ -/* -------------------------------------------------------------------- */ - if( src->datum_type == PJD_UNKNOWN - || dst->datum_type == PJD_UNKNOWN ) - return 0; - -/* -------------------------------------------------------------------- */ -/* Short cut if the datums are identical. */ -/* -------------------------------------------------------------------- */ - if( pj_compare_datums( src, dst ) ) - return 0; - - src_a = src->a_orig; - src_es = src->es_orig; - - dst_a = dst->a_orig; - dst_es = dst->es_orig; - -/* -------------------------------------------------------------------- */ -/* Create a temporary Z array if one is not provided. */ -/* -------------------------------------------------------------------- */ - if( z == NULL ) - { - size_t bytes = sizeof(double) * point_count * point_offset; - z = (double *) pj_malloc(bytes); - memset( z, 0, bytes ); - z_is_temp = TRUE; - } - -#define CHECK_RETURN(defn) {if( defn->ctx->last_errno != 0 && (defn->ctx->last_errno > 0 || get_transient_error_value(-defn->ctx->last_errno) == 0) ) { if( z_is_temp ) pj_dalloc(z); return defn->ctx->last_errno; }} - -/* -------------------------------------------------------------------- */ -/* If this datum requires grid shifts, then apply it to geodetic */ -/* coordinates. */ -/* -------------------------------------------------------------------- */ - if( src->datum_type == PJD_GRIDSHIFT ) - { - pj_apply_gridshift_2( src, 0, point_count, point_offset, x, y, z ); - CHECK_RETURN(src); - - src_a = SRS_WGS84_SEMIMAJOR; - src_es = SRS_WGS84_ESQUARED; - } - - if( dst->datum_type == PJD_GRIDSHIFT ) - { - dst_a = SRS_WGS84_SEMIMAJOR; - dst_es = SRS_WGS84_ESQUARED; - } - -/* ==================================================================== */ -/* Do we need to go through geocentric coordinates? */ -/* ==================================================================== */ - if( src_es != dst_es || src_a != dst_a - || src->datum_type == PJD_3PARAM - || src->datum_type == PJD_7PARAM - || dst->datum_type == PJD_3PARAM - || dst->datum_type == PJD_7PARAM) - { -/* -------------------------------------------------------------------- */ -/* Convert to geocentric coordinates. */ -/* -------------------------------------------------------------------- */ - src->ctx->last_errno = - pj_geodetic_to_geocentric( src_a, src_es, - point_count, point_offset, x, y, z ); - CHECK_RETURN(src); - -/* -------------------------------------------------------------------- */ -/* Convert between datums. */ -/* -------------------------------------------------------------------- */ - if( src->datum_type == PJD_3PARAM - || src->datum_type == PJD_7PARAM ) - { - pj_geocentric_to_wgs84( src, point_count, point_offset,x,y,z); - CHECK_RETURN(src); - } - - if( dst->datum_type == PJD_3PARAM - || dst->datum_type == PJD_7PARAM ) - { - pj_geocentric_from_wgs84( dst, point_count,point_offset,x,y,z); - CHECK_RETURN(dst); - } - -/* -------------------------------------------------------------------- */ -/* Convert back to geodetic coordinates. */ -/* -------------------------------------------------------------------- */ - dst->ctx->last_errno = - pj_geocentric_to_geodetic( dst_a, dst_es, - point_count, point_offset, x, y, z ); - CHECK_RETURN(dst); - } - -/* -------------------------------------------------------------------- */ -/* Apply grid shift to destination if required. */ -/* -------------------------------------------------------------------- */ - if( dst->datum_type == PJD_GRIDSHIFT ) - { - pj_apply_gridshift_2( dst, 1, point_count, point_offset, x, y, z ); - CHECK_RETURN(dst); - } - - if( z_is_temp ) - pj_dalloc( z ); - - return 0; -} - -/************************************************************************/ -/* adjust_axis() */ -/* */ -/* Normalize or de-normalized the x/y/z axes. The normal form */ -/* is "enu" (easting, northing, up). */ -/************************************************************************/ -static int adjust_axis( projCtx ctx, - const char *axis, int denormalize_flag, - long point_count, int point_offset, - double *x, double *y, double *z ) - -{ - double x_in, y_in, z_in = 0.0; - int i, i_axis; - - if( !denormalize_flag ) - { - for( i = 0; i < point_count; i++ ) - { - x_in = x[point_offset*i]; - y_in = y[point_offset*i]; - if( z ) - z_in = z[point_offset*i]; - - for( i_axis = 0; i_axis < 3; i_axis++ ) - { - double value; - - if( i_axis == 0 ) - value = x_in; - else if( i_axis == 1 ) - value = y_in; - else - value = z_in; - - switch( axis[i_axis] ) - { - case 'e': - x[point_offset*i] = value; - break; - case 'w': - x[point_offset*i] = -value; - break; - case 'n': - y[point_offset*i] = value; - break; - case 's': - y[point_offset*i] = -value; - break; - case 'u': - if( z ) - z[point_offset*i] = value; - break; - case 'd': - if( z ) - z[point_offset*i] = -value; - break; - default: - pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); - return PJD_ERR_AXIS; - } - } /* i_axis */ - } /* i (point) */ - } - - else /* denormalize */ - { - for( i = 0; i < point_count; i++ ) - { - x_in = x[point_offset*i]; - y_in = y[point_offset*i]; - if( z ) - z_in = z[point_offset*i]; - - for( i_axis = 0; i_axis < 3; i_axis++ ) - { - double *target; - - if( i_axis == 2 && z == NULL ) - continue; - - if( i_axis == 0 ) - target = x; - else if( i_axis == 1 ) - target = y; - else - target = z; - - switch( axis[i_axis] ) - { - case 'e': - target[point_offset*i] = x_in; break; - case 'w': - target[point_offset*i] = -x_in; break; - case 'n': - target[point_offset*i] = y_in; break; - case 's': - target[point_offset*i] = -y_in; break; - case 'u': - target[point_offset*i] = z_in; break; - case 'd': - target[point_offset*i] = -z_in; break; - default: - pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); - return PJD_ERR_AXIS; - } - } /* i_axis */ - } /* i (point) */ - } - - return 0; -} diff --git a/src/pj_transform.cpp b/src/pj_transform.cpp new file mode 100644 index 00000000..53429967 --- /dev/null +++ b/src/pj_transform.cpp @@ -0,0 +1,1060 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Perform overall coordinate system to coordinate system + * transformations (pj_transform() function) including reprojection + * and datum shifting. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2000, Frank Warmerdam + * + * 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 +#include +#include + +#include "projects.h" +#include "geocent.h" + + +/* Apply transformation to observation - in forward or inverse direction */ +/* Copied from proj.h */ +enum PJ_DIRECTION { + PJ_FWD = 1, /* Forward */ + PJ_IDENT = 0, /* Do nothing */ + PJ_INV = -1 /* Inverse */ +}; +typedef enum PJ_DIRECTION PJ_DIRECTION; + +/* Copied from proj.h FIXME */ +extern "C" int proj_errno_reset (const PJ *P); + + +static int adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, + long point_count, int point_offset, + double *x, double *y, double *z ); + +#ifndef SRS_WGS84_SEMIMAJOR +#define SRS_WGS84_SEMIMAJOR 6378137.0 +#endif + +#ifndef SRS_WGS84_ESQUARED +#define SRS_WGS84_ESQUARED 0.0066943799901413165 +#endif + +#define Dx_BF (defn->datum_params[0]) +#define Dy_BF (defn->datum_params[1]) +#define Dz_BF (defn->datum_params[2]) +#define Rx_BF (defn->datum_params[3]) +#define Ry_BF (defn->datum_params[4]) +#define Rz_BF (defn->datum_params[5]) +#define M_BF (defn->datum_params[6]) + +/* +** This table is intended to indicate for any given error code +** whether that error will occur for all locations (ie. +** it is a problem with the coordinate system as a whole) in which case the +** value would be 0, or if the problem is with the point being transformed +** in which case the value is 1. +** +** At some point we might want to move this array in with the error message +** list or something, but while experimenting with it this should be fine. +** +** +** NOTE (2017-10-01): Non-transient errors really should have resulted in a +** PJ==0 during initialization, and hence should be handled at the level +** before calling pj_transform. The only obvious example of the contrary +** appears to be the PJD_ERR_GRID_AREA case, which may also be taken to +** mean "no grids available" +** +** +*/ + +static const int transient_error[60] = { + /* 0 1 2 3 4 5 6 7 8 9 */ + /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + /* 50 to 59 */ 1, 0, 1, 0, 1, 1, 1, 1, 0, 0 }; + + +/* -------------------------------------------------------------------- */ +/* Read transient_error[] in a safe way. */ +/* -------------------------------------------------------------------- */ +static int get_transient_error_value(int pos_index) +{ + const int array_size = + (int)(sizeof(transient_error) / sizeof(transient_error[0])); + if( pos_index < 0 || pos_index >= array_size ) { + return 0; + } + return transient_error[pos_index]; +} + + +/* -------------------------------------------------------------------- */ +/* Transform unusual input coordinate axis orientation to */ +/* standard form if needed. */ +/* -------------------------------------------------------------------- */ +static int adjust_axes (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + /* Nothing to do? */ + if (0==strcmp(P->axis,"enu")) + return 0; + + return adjust_axis( P->ctx, P->axis, + dir==PJ_FWD ? 1: 0, n, dist, x, y, z ); +} + + + +/* ----------------------------------------------------------------------- */ +/* Transform geographic (lat/long) source coordinates to */ +/* cartesian ("geocentric"), if needed */ +/* ----------------------------------------------------------------------- */ +static int geographic_to_cartesian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + int res; + long i; + double fac = P->to_meter; + + /* Nothing to do? */ + if (!P->is_geocent) + return 0; + + if ( z == NULL ) { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + if (PJ_FWD==dir) { + fac = P->fr_meter; + res = pj_geodetic_to_geocentric( P->a_orig, P->es_orig, n, dist, x, y, z ); + if (res) + return res; + } + + if (fac != 1.0) { + for( i = 0; i < n; i++ ) { + if( x[dist*i] != HUGE_VAL ) { + x[dist*i] *= fac; + y[dist*i] *= fac; + z[dist*i] *= fac; + } + } + } + + if (PJ_FWD==dir) + return 0; + return pj_geocentric_to_geodetic( + P->a_orig, P->es_orig, + n, dist, + x, y, z + ); +} + + + + + + + + + + +/* -------------------------------------------------------------------- */ +/* Transform destination points to projection coordinates, if */ +/* desired. */ +/* */ +/* Ought to fold this into projected_to_geographic */ +/* -------------------------------------------------------------------- */ +static int geographic_to_projected (PJ *P, long n, int dist, double *x, double *y, double *z) { + long i; + + /* Nothing to do? */ + if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) + return 0; + if (P->is_geocent) + return 0; + + if(P->fwd3d != NULL && !(z == NULL && P->is_latlong)) + { + /* Three dimensions must be defined */ + if ( z == NULL) + { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < n; i++ ) + { + XYZ projected_loc; + LPZ geodetic_loc; + + geodetic_loc.u = x[dist*i]; + geodetic_loc.v = y[dist*i]; + geodetic_loc.w = z[dist*i]; + + if (geodetic_loc.u == HUGE_VAL) + continue; + + proj_errno_reset( P ); + projected_loc = pj_fwd3d( geodetic_loc, P); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + projected_loc.u = HUGE_VAL; + projected_loc.v = HUGE_VAL; + projected_loc.w = HUGE_VAL; + } + } + + x[dist*i] = projected_loc.u; + y[dist*i] = projected_loc.v; + z[dist*i] = projected_loc.w; + } + return 0; + } + + for( i = 0; i ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + projected_loc.u = HUGE_VAL; + projected_loc.v = HUGE_VAL; + } + } + + x[dist*i] = projected_loc.u; + y[dist*i] = projected_loc.v; + } + return 0; +} + + + + + +/* ----------------------------------------------------------------------- */ +/* Transform projected source coordinates to lat/long, if needed */ +/* ----------------------------------------------------------------------- */ +static int projected_to_geographic (PJ *P, long n, int dist, double *x, double *y, double *z) { + long i; + + /* Nothing to do? */ + if (P->is_latlong && !P->geoc && P->vto_meter == 1.0) + return 0; + if (P->is_geocent) + return 0; + + /* Check first if projection is invertible. */ + if( (P->inv3d == NULL) && (P->inv == NULL)) + { + pj_ctx_set_errno(pj_get_ctx(P), PJD_ERR_NON_CONV_INV_MERI_DIST); + pj_log( pj_get_ctx(P), PJ_LOG_ERROR, + "pj_transform(): source projection not invertable" ); + return PJD_ERR_NON_CONV_INV_MERI_DIST; + } + + /* If invertible - First try inv3d if defined */ + if (P->inv3d != NULL && !(z == NULL && P->is_latlong)) + { + /* Three dimensions must be defined */ + if ( z == NULL) + { + pj_ctx_set_errno( pj_get_ctx(P), PJD_ERR_GEOCENTRIC); + return PJD_ERR_GEOCENTRIC; + } + + for (i=0; i < n; i++) + { + XYZ projected_loc; + XYZ geodetic_loc; + + projected_loc.u = x[dist*i]; + projected_loc.v = y[dist*i]; + projected_loc.w = z[dist*i]; + + if (projected_loc.u == HUGE_VAL) + continue; + + proj_errno_reset( P ); + geodetic_loc = pj_inv3d(projected_loc, P); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + geodetic_loc.u = HUGE_VAL; + geodetic_loc.v = HUGE_VAL; + geodetic_loc.w = HUGE_VAL; + } + } + + x[dist*i] = geodetic_loc.u; + y[dist*i] = geodetic_loc.v; + z[dist*i] = geodetic_loc.w; + + } + return 0; + } + + /* Fallback to the original PROJ.4 API 2d inversion - inv */ + for( i = 0; i < n; i++ ) { + XY projected_loc; + LP geodetic_loc; + + projected_loc.u = x[dist*i]; + projected_loc.v = y[dist*i]; + + if( projected_loc.u == HUGE_VAL ) + continue; + + proj_errno_reset( P ); + geodetic_loc = pj_inv( projected_loc, P ); + if( P->ctx->last_errno != 0 ) + { + if( (P->ctx->last_errno != EDOM + && P->ctx->last_errno != ERANGE) + && (P->ctx->last_errno > 0 + || P->ctx->last_errno < -44 || n == 1 + || get_transient_error_value(-P->ctx->last_errno) == 0 ) ) + { + return P->ctx->last_errno; + } + else + { + geodetic_loc.u = HUGE_VAL; + geodetic_loc.v = HUGE_VAL; + } + } + + x[dist*i] = geodetic_loc.u; + y[dist*i] = geodetic_loc.v; + } + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Adjust for the prime meridian if needed. */ +/* -------------------------------------------------------------------- */ +static int prime_meridian (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x) { + int i; + double pm = P->from_greenwich; + + /* Nothing to do? */ + if (pm==0.0) + return 0; + if (!(P->is_geocent || P->is_latlong)) + return 0; + + if (dir==PJ_FWD) + pm = -pm; + + for (i = 0; i < n; i++) + if (x[dist*i] != HUGE_VAL) + x[dist*i] += pm; + + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Adjust for vertical scale factor if needed */ +/* -------------------------------------------------------------------- */ +static int height_unit (PJ *P, PJ_DIRECTION dir, long n, int dist, double *z) { + int i; + double fac = P->vto_meter; + + if (PJ_FWD==dir) + fac = P->vfr_meter; + + /* Nothing to do? */ + if (fac==1.0) + return 0; + if (0==z) + return 0; + if (P->is_latlong) + return 0; /* done in pj_inv3d() / pj_fwd3d() */ + + for (i = 0; i < n; i++) + if (z[dist*i] != HUGE_VAL ) + z[dist*i] *= fac; + + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Transform to ellipsoidal heights if needed */ +/* -------------------------------------------------------------------- */ +static int geometric_to_orthometric (PJ *P, PJ_DIRECTION dir, long n, int dist, double *x, double *y, double *z) { + int err; + if (0==P->has_geoid_vgrids) + return 0; + if (z==0) + return PJD_ERR_GEOCENTRIC; + err = pj_apply_vgridshift (P, "sgeoidgrids", + &(P->vgridlist_geoid), + &(P->vgridlist_geoid_count), + dir==PJ_FWD ? 1 : 0, n, dist, x, y, z ); + if (err) + return pj_ctx_get_errno(P->ctx); + return 0; +} + + + +/* -------------------------------------------------------------------- */ +/* Convert datums if needed, and possible. */ +/* -------------------------------------------------------------------- */ +static int datum_transform (PJ *P, PJ *Q, long n, int dist, double *x, double *y, double *z) { + if (0==pj_datum_transform (P, Q, n, dist, x, y, z)) + return 0; + if (P->ctx->last_errno) + return P->ctx->last_errno; + return Q->ctx->last_errno; +} + + + + + +/* -------------------------------------------------------------------- */ +/* If a wrapping center other than 0 is provided, rewrap around */ +/* the suggested center (for latlong coordinate systems only). */ +/* -------------------------------------------------------------------- */ +static int long_wrap (PJ *P, long n, int dist, double *x) { + long i; + + /* Nothing to do? */ + if (P->is_geocent) + return 0; + if (!P->is_long_wrap_set) + return 0; + if (!P->is_latlong) + return 0; + + for (i = 0; i < n; i++ ) { + double val = x[dist*i]; + if (val == HUGE_VAL) + continue; + + /* Get fast in ] -2 PI, 2 PI [ range */ + val = fmod(val, M_TWOPI); + while( val < P->long_wrap_center - M_PI ) + val += M_TWOPI; + while( val > P->long_wrap_center + M_PI ) + val -= M_TWOPI; + x[dist*i] = val; + } + return 0; +} + + + +/************************************************************************/ +/* pj_transform() */ +/* */ +/* Currently this function doesn't recognise if two projections */ +/* are identical (to short circuit reprojection) because it is */ +/* difficult to compare PJ structures (since there are some */ +/* projection specific components). */ +/************************************************************************/ + +int pj_transform( + PJ *src, PJ *dst, + long point_count, int point_offset, + double *x, double *y, double *z +){ + int err; + + src->ctx->last_errno = 0; + dst->ctx->last_errno = 0; + + if( point_offset == 0 ) + point_offset = 1; + + /* Bring input to "normal form": longitude, latitude, ellipsoidal height */ + + err = adjust_axes (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + err = geographic_to_cartesian (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + err = projected_to_geographic (src, point_count, point_offset, x, y, z); + if (err) + return err; + err = prime_meridian (src, PJ_INV, point_count, point_offset, x); + if (err) + return err; + err = height_unit (src, PJ_INV, point_count, point_offset, z); + if (err) + return err; + err = geometric_to_orthometric (src, PJ_INV, point_count, point_offset, x, y, z); + if (err) + return err; + + /* At the center of the process we do the datum shift (if needed) */ + + err = datum_transform(src, dst, point_count, point_offset, x, y, z ); + if (err) + return err; + + /* Now get out on the other side: Bring "normal form" to output form */ + + err = geometric_to_orthometric (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + err = height_unit (dst, PJ_FWD, point_count, point_offset, z); + if (err) + return err; + err = prime_meridian (dst, PJ_FWD, point_count, point_offset, x); + if (err) + return err; + err = geographic_to_cartesian (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + err = geographic_to_projected (dst, point_count, point_offset, x, y, z); + if (err) + return err; + err = long_wrap (dst, point_count, point_offset, x); + if (err) + return err; + err = adjust_axes (dst, PJ_FWD, point_count, point_offset, x, y, z); + if (err) + return err; + + return 0; +} + + + +/************************************************************************/ +/* pj_geodetic_to_geocentric() */ +/************************************************************************/ + +int pj_geodetic_to_geocentric( double a, double es, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double b; + int i; + GeocentricInfo gi; + int ret_errno = 0; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) + { + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], + x+io, y+io, z+io ) != 0 ) + { + ret_errno = PJD_ERR_LAT_OR_LON_EXCEED_LIMIT; + x[io] = y[io] = HUGE_VAL; + /* but keep processing points! */ + } + } + + return ret_errno; +} + +/************************************************************************/ +/* pj_geocentric_to_geodetic() */ +/************************************************************************/ + +int pj_geocentric_to_geodetic( double a, double es, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double b; + int i; + GeocentricInfo gi; + + if( es == 0.0 ) + b = a; + else + b = a * sqrt(1-es); + + if( pj_Set_Geocentric_Parameters( &gi, a, b ) != 0 ) + { + return PJD_ERR_GEOCENTRIC; + } + + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], + y+io, x+io, z+io ); + } + + return 0; +} + +/************************************************************************/ +/* pj_compare_datums() */ +/* */ +/* Returns TRUE if the two datums are identical, otherwise */ +/* FALSE. */ +/************************************************************************/ + +int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) + +{ + if( srcdefn->datum_type != dstdefn->datum_type ) + { + return 0; + } + else if( srcdefn->a_orig != dstdefn->a_orig + || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 ) + { + /* the tolerance for es is to ensure that GRS80 and WGS84 are + considered identical */ + return 0; + } + else if( srcdefn->datum_type == PJD_3PARAM ) + { + return (srcdefn->datum_params[0] == dstdefn->datum_params[0] + && srcdefn->datum_params[1] == dstdefn->datum_params[1] + && srcdefn->datum_params[2] == dstdefn->datum_params[2]); + } + else if( srcdefn->datum_type == PJD_7PARAM ) + { + return (srcdefn->datum_params[0] == dstdefn->datum_params[0] + && srcdefn->datum_params[1] == dstdefn->datum_params[1] + && srcdefn->datum_params[2] == dstdefn->datum_params[2] + && srcdefn->datum_params[3] == dstdefn->datum_params[3] + && srcdefn->datum_params[4] == dstdefn->datum_params[4] + && srcdefn->datum_params[5] == dstdefn->datum_params[5] + && srcdefn->datum_params[6] == dstdefn->datum_params[6]); + } + else if( srcdefn->datum_type == PJD_GRIDSHIFT ) + { + const char* srcnadgrids = + pj_param(srcdefn->ctx, srcdefn->params,"snadgrids").s; + const char* dstnadgrids = + pj_param(dstdefn->ctx, dstdefn->params,"snadgrids").s; + return srcnadgrids != 0 && dstnadgrids != 0 && + strcmp( srcnadgrids, dstnadgrids ) == 0; + } + else + return 1; +} + +/************************************************************************/ +/* pj_geocentic_to_wgs84() */ +/************************************************************************/ + +static +int pj_geocentric_to_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + x[io] = x[io] + Dx_BF; + y[io] = y[io] + Dy_BF; + z[io] = z[io] + Dz_BF; + } + } + else if( defn->datum_type == PJD_7PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + double x_out, y_out, z_out; + + if( x[io] == HUGE_VAL ) + continue; + + x_out = M_BF*( x[io] - Rz_BF*y[io] + Ry_BF*z[io]) + Dx_BF; + y_out = M_BF*( Rz_BF*x[io] + y[io] - Rx_BF*z[io]) + Dy_BF; + z_out = M_BF*(-Ry_BF*x[io] + Rx_BF*y[io] + z[io]) + Dz_BF; + + x[io] = x_out; + y[io] = y_out; + z[io] = z_out; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_geocentic_from_wgs84() */ +/************************************************************************/ + +static +int pj_geocentric_from_wgs84( PJ *defn, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + int i; + + if( defn->datum_type == PJD_3PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + + if( x[io] == HUGE_VAL ) + continue; + + x[io] = x[io] - Dx_BF; + y[io] = y[io] - Dy_BF; + z[io] = z[io] - Dz_BF; + } + } + else if( defn->datum_type == PJD_7PARAM ) + { + for( i = 0; i < point_count; i++ ) + { + long io = i * point_offset; + double x_tmp, y_tmp, z_tmp; + + if( x[io] == HUGE_VAL ) + continue; + + x_tmp = (x[io] - Dx_BF) / M_BF; + y_tmp = (y[io] - Dy_BF) / M_BF; + z_tmp = (z[io] - Dz_BF) / M_BF; + + x[io] = x_tmp + Rz_BF*y_tmp - Ry_BF*z_tmp; + y[io] = -Rz_BF*x_tmp + y_tmp + Rx_BF*z_tmp; + z[io] = Ry_BF*x_tmp - Rx_BF*y_tmp + z_tmp; + } + } + + return 0; +} + +/************************************************************************/ +/* pj_datum_transform() */ +/* */ +/* The input should be long/lat/z coordinates in radians in the */ +/* source datum, and the output should be long/lat/z */ +/* coordinates in radians in the destination datum. */ +/************************************************************************/ + +int pj_datum_transform( PJ *src, PJ *dst, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double src_a, src_es, dst_a, dst_es; + int z_is_temp = FALSE; + +/* -------------------------------------------------------------------- */ +/* We cannot do any meaningful datum transformation if either */ +/* the source or destination are of an unknown datum type */ +/* (ie. only a +ellps declaration, no +datum). This is new */ +/* behavior for PROJ 4.6.0. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_UNKNOWN + || dst->datum_type == PJD_UNKNOWN ) + return 0; + +/* -------------------------------------------------------------------- */ +/* Short cut if the datums are identical. */ +/* -------------------------------------------------------------------- */ + if( pj_compare_datums( src, dst ) ) + return 0; + + src_a = src->a_orig; + src_es = src->es_orig; + + dst_a = dst->a_orig; + dst_es = dst->es_orig; + +/* -------------------------------------------------------------------- */ +/* Create a temporary Z array if one is not provided. */ +/* -------------------------------------------------------------------- */ + if( z == NULL ) + { + size_t bytes = sizeof(double) * point_count * point_offset; + z = (double *) pj_malloc(bytes); + memset( z, 0, bytes ); + z_is_temp = TRUE; + } + +#define CHECK_RETURN(defn) {if( defn->ctx->last_errno != 0 && (defn->ctx->last_errno > 0 || get_transient_error_value(-defn->ctx->last_errno) == 0) ) { if( z_is_temp ) pj_dalloc(z); return defn->ctx->last_errno; }} + +/* -------------------------------------------------------------------- */ +/* If this datum requires grid shifts, then apply it to geodetic */ +/* coordinates. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift_2( src, 0, point_count, point_offset, x, y, z ); + CHECK_RETURN(src); + + src_a = SRS_WGS84_SEMIMAJOR; + src_es = SRS_WGS84_ESQUARED; + } + + if( dst->datum_type == PJD_GRIDSHIFT ) + { + dst_a = SRS_WGS84_SEMIMAJOR; + dst_es = SRS_WGS84_ESQUARED; + } + +/* ==================================================================== */ +/* Do we need to go through geocentric coordinates? */ +/* ==================================================================== */ + if( src_es != dst_es || src_a != dst_a + || src->datum_type == PJD_3PARAM + || src->datum_type == PJD_7PARAM + || dst->datum_type == PJD_3PARAM + || dst->datum_type == PJD_7PARAM) + { +/* -------------------------------------------------------------------- */ +/* Convert to geocentric coordinates. */ +/* -------------------------------------------------------------------- */ + src->ctx->last_errno = + pj_geodetic_to_geocentric( src_a, src_es, + point_count, point_offset, x, y, z ); + CHECK_RETURN(src); + +/* -------------------------------------------------------------------- */ +/* Convert between datums. */ +/* -------------------------------------------------------------------- */ + if( src->datum_type == PJD_3PARAM + || src->datum_type == PJD_7PARAM ) + { + pj_geocentric_to_wgs84( src, point_count, point_offset,x,y,z); + CHECK_RETURN(src); + } + + if( dst->datum_type == PJD_3PARAM + || dst->datum_type == PJD_7PARAM ) + { + pj_geocentric_from_wgs84( dst, point_count,point_offset,x,y,z); + CHECK_RETURN(dst); + } + +/* -------------------------------------------------------------------- */ +/* Convert back to geodetic coordinates. */ +/* -------------------------------------------------------------------- */ + dst->ctx->last_errno = + pj_geocentric_to_geodetic( dst_a, dst_es, + point_count, point_offset, x, y, z ); + CHECK_RETURN(dst); + } + +/* -------------------------------------------------------------------- */ +/* Apply grid shift to destination if required. */ +/* -------------------------------------------------------------------- */ + if( dst->datum_type == PJD_GRIDSHIFT ) + { + pj_apply_gridshift_2( dst, 1, point_count, point_offset, x, y, z ); + CHECK_RETURN(dst); + } + + if( z_is_temp ) + pj_dalloc( z ); + + return 0; +} + +/************************************************************************/ +/* adjust_axis() */ +/* */ +/* Normalize or de-normalized the x/y/z axes. The normal form */ +/* is "enu" (easting, northing, up). */ +/************************************************************************/ +static int adjust_axis( projCtx ctx, + const char *axis, int denormalize_flag, + long point_count, int point_offset, + double *x, double *y, double *z ) + +{ + double x_in, y_in, z_in = 0.0; + int i, i_axis; + + if( !denormalize_flag ) + { + for( i = 0; i < point_count; i++ ) + { + x_in = x[point_offset*i]; + y_in = y[point_offset*i]; + if( z ) + z_in = z[point_offset*i]; + + for( i_axis = 0; i_axis < 3; i_axis++ ) + { + double value; + + if( i_axis == 0 ) + value = x_in; + else if( i_axis == 1 ) + value = y_in; + else + value = z_in; + + switch( axis[i_axis] ) + { + case 'e': + x[point_offset*i] = value; + break; + case 'w': + x[point_offset*i] = -value; + break; + case 'n': + y[point_offset*i] = value; + break; + case 's': + y[point_offset*i] = -value; + break; + case 'u': + if( z ) + z[point_offset*i] = value; + break; + case 'd': + if( z ) + z[point_offset*i] = -value; + break; + default: + pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); + return PJD_ERR_AXIS; + } + } /* i_axis */ + } /* i (point) */ + } + + else /* denormalize */ + { + for( i = 0; i < point_count; i++ ) + { + x_in = x[point_offset*i]; + y_in = y[point_offset*i]; + if( z ) + z_in = z[point_offset*i]; + + for( i_axis = 0; i_axis < 3; i_axis++ ) + { + double *target; + + if( i_axis == 2 && z == NULL ) + continue; + + if( i_axis == 0 ) + target = x; + else if( i_axis == 1 ) + target = y; + else + target = z; + + switch( axis[i_axis] ) + { + case 'e': + target[point_offset*i] = x_in; break; + case 'w': + target[point_offset*i] = -x_in; break; + case 'n': + target[point_offset*i] = y_in; break; + case 's': + target[point_offset*i] = -y_in; break; + case 'u': + target[point_offset*i] = z_in; break; + case 'd': + target[point_offset*i] = -z_in; break; + default: + pj_ctx_set_errno( ctx, PJD_ERR_AXIS ); + return PJD_ERR_AXIS; + } + } /* i_axis */ + } /* i (point) */ + } + + return 0; +} diff --git a/src/pj_tsfn.c b/src/pj_tsfn.c deleted file mode 100644 index ea3b896d..00000000 --- a/src/pj_tsfn.c +++ /dev/null @@ -1,16 +0,0 @@ -/* determine small t */ -#include -#include "projects.h" - -double pj_tsfn(double phi, double sinphi, double e) { - double denominator; - sinphi *= e; - - /* avoid zero division, fail gracefully */ - denominator = 1.0 + sinphi; - if (denominator == 0.0) - return HUGE_VAL; - - return (tan (.5 * (M_HALFPI - phi)) / - pow((1. - sinphi) / (denominator), .5 * e)); -} diff --git a/src/pj_tsfn.cpp b/src/pj_tsfn.cpp new file mode 100644 index 00000000..ea3b896d --- /dev/null +++ b/src/pj_tsfn.cpp @@ -0,0 +1,16 @@ +/* determine small t */ +#include +#include "projects.h" + +double pj_tsfn(double phi, double sinphi, double e) { + double denominator; + sinphi *= e; + + /* avoid zero division, fail gracefully */ + denominator = 1.0 + sinphi; + if (denominator == 0.0) + return HUGE_VAL; + + return (tan (.5 * (M_HALFPI - phi)) / + pow((1. - sinphi) / (denominator), .5 * e)); +} diff --git a/src/pj_units.c b/src/pj_units.c deleted file mode 100644 index 877758a3..00000000 --- a/src/pj_units.c +++ /dev/null @@ -1,58 +0,0 @@ -/* definition of standard cartesian units */ - -#include - -#include "proj.h" - -#define PJ_UNITS__ -#include "projects.h" - -/* Field 2 that contains the multiplier to convert named units to meters -** may be expressed by either a simple floating point constant or a -** numerator/denomenator values (e.g. 1/1000) */ -static const struct PJ_UNITS -pj_units[] = { - {"km", "1000", "Kilometer", 1000.0}, - {"m", "1", "Meter", 1.0}, - {"dm", "1/10", "Decimeter", 0.1}, - {"cm", "1/100", "Centimeter", 0.01}, - {"mm", "1/1000", "Millimeter", 0.001}, - {"kmi", "1852", "International Nautical Mile", 1852.0}, - {"in", "0.0254", "International Inch", 0.0254}, - {"ft", "0.3048", "International Foot", 0.3048}, - {"yd", "0.9144", "International Yard", 0.9144}, - {"mi", "1609.344", "International Statute Mile", 1609.344}, - {"fath", "1.8288", "International Fathom", 1.8288}, - {"ch", "20.1168", "International Chain", 20.1168}, - {"link", "0.201168", "International Link", 0.201168}, - {"us-in", "1/39.37", "U.S. Surveyor's Inch", 100/3937.0}, - {"us-ft", "0.304800609601219", "U.S. Surveyor's Foot", 1200/3937.0}, - {"us-yd", "0.914401828803658", "U.S. Surveyor's Yard", 3600/3937.0}, - {"us-ch", "20.11684023368047", "U.S. Surveyor's Chain", 79200/3937.0}, - {"us-mi", "1609.347218694437", "U.S. Surveyor's Statute Mile", 6336000/3937.0}, - {"ind-yd", "0.91439523", "Indian Yard", 0.91439523}, - {"ind-ft", "0.30479841", "Indian Foot", 0.30479841}, - {"ind-ch", "20.11669506", "Indian Chain", 20.11669506}, - {NULL, NULL, NULL, 0.0} -}; - -const PJ_UNITS *proj_list_units() -{ - return pj_units; -} - -/* M_PI / 200 */ -#define GRAD_TO_RAD 0.015707963267948967 - -const struct PJ_UNITS -pj_angular_units[] = { - {"rad", "1.0", "Radian", 1.0}, - {"deg", "0.017453292519943296", "Degree", DEG_TO_RAD}, - {"grad", "0.015707963267948967", "Grad", GRAD_TO_RAD}, - {NULL, NULL, NULL, 0.0} -}; - -const PJ_UNITS *proj_list_angular_units() -{ - return pj_angular_units; -} diff --git a/src/pj_units.cpp b/src/pj_units.cpp new file mode 100644 index 00000000..877758a3 --- /dev/null +++ b/src/pj_units.cpp @@ -0,0 +1,58 @@ +/* definition of standard cartesian units */ + +#include + +#include "proj.h" + +#define PJ_UNITS__ +#include "projects.h" + +/* Field 2 that contains the multiplier to convert named units to meters +** may be expressed by either a simple floating point constant or a +** numerator/denomenator values (e.g. 1/1000) */ +static const struct PJ_UNITS +pj_units[] = { + {"km", "1000", "Kilometer", 1000.0}, + {"m", "1", "Meter", 1.0}, + {"dm", "1/10", "Decimeter", 0.1}, + {"cm", "1/100", "Centimeter", 0.01}, + {"mm", "1/1000", "Millimeter", 0.001}, + {"kmi", "1852", "International Nautical Mile", 1852.0}, + {"in", "0.0254", "International Inch", 0.0254}, + {"ft", "0.3048", "International Foot", 0.3048}, + {"yd", "0.9144", "International Yard", 0.9144}, + {"mi", "1609.344", "International Statute Mile", 1609.344}, + {"fath", "1.8288", "International Fathom", 1.8288}, + {"ch", "20.1168", "International Chain", 20.1168}, + {"link", "0.201168", "International Link", 0.201168}, + {"us-in", "1/39.37", "U.S. Surveyor's Inch", 100/3937.0}, + {"us-ft", "0.304800609601219", "U.S. Surveyor's Foot", 1200/3937.0}, + {"us-yd", "0.914401828803658", "U.S. Surveyor's Yard", 3600/3937.0}, + {"us-ch", "20.11684023368047", "U.S. Surveyor's Chain", 79200/3937.0}, + {"us-mi", "1609.347218694437", "U.S. Surveyor's Statute Mile", 6336000/3937.0}, + {"ind-yd", "0.91439523", "Indian Yard", 0.91439523}, + {"ind-ft", "0.30479841", "Indian Foot", 0.30479841}, + {"ind-ch", "20.11669506", "Indian Chain", 20.11669506}, + {NULL, NULL, NULL, 0.0} +}; + +const PJ_UNITS *proj_list_units() +{ + return pj_units; +} + +/* M_PI / 200 */ +#define GRAD_TO_RAD 0.015707963267948967 + +const struct PJ_UNITS +pj_angular_units[] = { + {"rad", "1.0", "Radian", 1.0}, + {"deg", "0.017453292519943296", "Degree", DEG_TO_RAD}, + {"grad", "0.015707963267948967", "Grad", GRAD_TO_RAD}, + {NULL, NULL, NULL, 0.0} +}; + +const PJ_UNITS *proj_list_angular_units() +{ + return pj_angular_units; +} diff --git a/src/pj_utils.c b/src/pj_utils.c deleted file mode 100644 index 81a80b45..00000000 --- a/src/pj_utils.c +++ /dev/null @@ -1,181 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Some utility functions we don't want to bother putting in - * their own source files. - * Author: Frank Warmerdam, warmerdam@pobox.com - * - ****************************************************************************** - * Copyright (c) 2001, Frank Warmerdam - * - * 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. - *****************************************************************************/ - -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -/************************************************************************/ -/* pj_is_latlong() */ -/* */ -/* Returns TRUE if this coordinate system object is */ -/* geographic. */ -/************************************************************************/ - -int pj_is_latlong( PJ *pj ) - -{ - return pj == NULL || pj->is_latlong; -} - -/************************************************************************/ -/* pj_is_geocent() */ -/* */ -/* Returns TRUE if this coordinate system object is geocentric. */ -/************************************************************************/ - -int pj_is_geocent( PJ *pj ) - -{ - return pj != NULL && pj->is_geocent; -} - -/************************************************************************/ -/* pj_latlong_from_proj() */ -/* */ -/* Return a PJ* definition defining the lat/long coordinate */ -/* system on which a projection is based. If the coordinate */ -/* system passed in is latlong, a clone of the same will be */ -/* returned. */ -/************************************************************************/ - -PJ *pj_latlong_from_proj( PJ *pj_in ) - -{ - char defn[512]; - int got_datum = FALSE; - - pj_errno = 0; - strcpy( defn, "+proj=latlong" ); - - if( pj_param(pj_in->ctx, pj_in->params, "tdatum").i ) - { - got_datum = TRUE; - sprintf( defn+strlen(defn), " +datum=%s", - pj_param(pj_in->ctx, pj_in->params,"sdatum").s ); - } - else if( pj_param(pj_in->ctx, pj_in->params, "tellps").i ) - { - sprintf( defn+strlen(defn), " +ellps=%s", - pj_param(pj_in->ctx, pj_in->params,"sellps").s ); - } - else if( pj_param(pj_in->ctx,pj_in->params, "ta").i ) - { - sprintf( defn+strlen(defn), " +a=%s", - pj_param(pj_in->ctx,pj_in->params,"sa").s ); - - if( pj_param(pj_in->ctx,pj_in->params, "tb").i ) - sprintf( defn+strlen(defn), " +b=%s", - pj_param(pj_in->ctx,pj_in->params,"sb").s ); - else if( pj_param(pj_in->ctx,pj_in->params, "tes").i ) - sprintf( defn+strlen(defn), " +es=%s", - pj_param(pj_in->ctx,pj_in->params,"ses").s ); - else if( pj_param(pj_in->ctx,pj_in->params, "tf").i ) - sprintf( defn+strlen(defn), " +f=%s", - pj_param(pj_in->ctx,pj_in->params,"sf").s ); - else - { - char* ptr = defn+strlen(defn); - sprintf( ptr, " +es=%.16g", pj_in->es ); - /* TODO later: use C++ ostringstream with imbue(std::locale::classic()) */ - /* to be locale unaware */ - for(; *ptr; ptr++) - { - if( *ptr == ',' ) - *ptr = '.'; - } - } - } - else - { - pj_ctx_set_errno( pj_in->ctx, PJD_ERR_MAJOR_AXIS_NOT_GIVEN ); - - return NULL; - } - - if( !got_datum ) - { - if( pj_param(pj_in->ctx,pj_in->params, "ttowgs84").i ) - sprintf( defn+strlen(defn), " +towgs84=%s", - pj_param(pj_in->ctx,pj_in->params,"stowgs84").s ); - - if( pj_param(pj_in->ctx,pj_in->params, "tnadgrids").i ) - sprintf( defn+strlen(defn), " +nadgrids=%s", - pj_param(pj_in->ctx,pj_in->params,"snadgrids").s ); - } - - /* copy over some other information related to ellipsoid */ - if( pj_param(pj_in->ctx,pj_in->params, "tR").i ) - sprintf( defn+strlen(defn), " +R=%s", - pj_param(pj_in->ctx,pj_in->params,"sR").s ); - - if( pj_param(pj_in->ctx,pj_in->params, "tR_A").i ) - sprintf( defn+strlen(defn), " +R_A" ); - - if( pj_param(pj_in->ctx,pj_in->params, "tR_V").i ) - sprintf( defn+strlen(defn), " +R_V" ); - - if( pj_param(pj_in->ctx,pj_in->params, "tR_a").i ) - sprintf( defn+strlen(defn), " +R_a" ); - - if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_a").i ) - sprintf( defn+strlen(defn), " +R_lat_a=%s", - pj_param(pj_in->ctx,pj_in->params,"sR_lat_a").s ); - - if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_g").i ) - sprintf( defn+strlen(defn), " +R_lat_g=%s", - pj_param(pj_in->ctx,pj_in->params,"sR_lat_g").s ); - - /* copy over prime meridian */ - if( pj_param(pj_in->ctx,pj_in->params, "tpm").i ) - sprintf( defn+strlen(defn), " +pm=%s", - pj_param(pj_in->ctx,pj_in->params,"spm").s ); - - return pj_init_plus_ctx( pj_in->ctx, defn ); -} - -/************************************************************************/ -/* pj_get_spheroid_defn() */ -/* */ -/* Fetch the internal definition of the spheroid. Note that */ -/* you can compute "b" from eccentricity_squared as: */ -/* */ -/* b = a * sqrt(1 - es) */ -/************************************************************************/ - -void pj_get_spheroid_defn(projPJ defn, double *major_axis, double *eccentricity_squared) -{ - if ( major_axis ) - *major_axis = defn->a; - - if ( eccentricity_squared ) - *eccentricity_squared = defn->es; -} diff --git a/src/pj_utils.cpp b/src/pj_utils.cpp new file mode 100644 index 00000000..81a80b45 --- /dev/null +++ b/src/pj_utils.cpp @@ -0,0 +1,181 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Some utility functions we don't want to bother putting in + * their own source files. + * Author: Frank Warmerdam, warmerdam@pobox.com + * + ****************************************************************************** + * Copyright (c) 2001, Frank Warmerdam + * + * 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. + *****************************************************************************/ + +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +/************************************************************************/ +/* pj_is_latlong() */ +/* */ +/* Returns TRUE if this coordinate system object is */ +/* geographic. */ +/************************************************************************/ + +int pj_is_latlong( PJ *pj ) + +{ + return pj == NULL || pj->is_latlong; +} + +/************************************************************************/ +/* pj_is_geocent() */ +/* */ +/* Returns TRUE if this coordinate system object is geocentric. */ +/************************************************************************/ + +int pj_is_geocent( PJ *pj ) + +{ + return pj != NULL && pj->is_geocent; +} + +/************************************************************************/ +/* pj_latlong_from_proj() */ +/* */ +/* Return a PJ* definition defining the lat/long coordinate */ +/* system on which a projection is based. If the coordinate */ +/* system passed in is latlong, a clone of the same will be */ +/* returned. */ +/************************************************************************/ + +PJ *pj_latlong_from_proj( PJ *pj_in ) + +{ + char defn[512]; + int got_datum = FALSE; + + pj_errno = 0; + strcpy( defn, "+proj=latlong" ); + + if( pj_param(pj_in->ctx, pj_in->params, "tdatum").i ) + { + got_datum = TRUE; + sprintf( defn+strlen(defn), " +datum=%s", + pj_param(pj_in->ctx, pj_in->params,"sdatum").s ); + } + else if( pj_param(pj_in->ctx, pj_in->params, "tellps").i ) + { + sprintf( defn+strlen(defn), " +ellps=%s", + pj_param(pj_in->ctx, pj_in->params,"sellps").s ); + } + else if( pj_param(pj_in->ctx,pj_in->params, "ta").i ) + { + sprintf( defn+strlen(defn), " +a=%s", + pj_param(pj_in->ctx,pj_in->params,"sa").s ); + + if( pj_param(pj_in->ctx,pj_in->params, "tb").i ) + sprintf( defn+strlen(defn), " +b=%s", + pj_param(pj_in->ctx,pj_in->params,"sb").s ); + else if( pj_param(pj_in->ctx,pj_in->params, "tes").i ) + sprintf( defn+strlen(defn), " +es=%s", + pj_param(pj_in->ctx,pj_in->params,"ses").s ); + else if( pj_param(pj_in->ctx,pj_in->params, "tf").i ) + sprintf( defn+strlen(defn), " +f=%s", + pj_param(pj_in->ctx,pj_in->params,"sf").s ); + else + { + char* ptr = defn+strlen(defn); + sprintf( ptr, " +es=%.16g", pj_in->es ); + /* TODO later: use C++ ostringstream with imbue(std::locale::classic()) */ + /* to be locale unaware */ + for(; *ptr; ptr++) + { + if( *ptr == ',' ) + *ptr = '.'; + } + } + } + else + { + pj_ctx_set_errno( pj_in->ctx, PJD_ERR_MAJOR_AXIS_NOT_GIVEN ); + + return NULL; + } + + if( !got_datum ) + { + if( pj_param(pj_in->ctx,pj_in->params, "ttowgs84").i ) + sprintf( defn+strlen(defn), " +towgs84=%s", + pj_param(pj_in->ctx,pj_in->params,"stowgs84").s ); + + if( pj_param(pj_in->ctx,pj_in->params, "tnadgrids").i ) + sprintf( defn+strlen(defn), " +nadgrids=%s", + pj_param(pj_in->ctx,pj_in->params,"snadgrids").s ); + } + + /* copy over some other information related to ellipsoid */ + if( pj_param(pj_in->ctx,pj_in->params, "tR").i ) + sprintf( defn+strlen(defn), " +R=%s", + pj_param(pj_in->ctx,pj_in->params,"sR").s ); + + if( pj_param(pj_in->ctx,pj_in->params, "tR_A").i ) + sprintf( defn+strlen(defn), " +R_A" ); + + if( pj_param(pj_in->ctx,pj_in->params, "tR_V").i ) + sprintf( defn+strlen(defn), " +R_V" ); + + if( pj_param(pj_in->ctx,pj_in->params, "tR_a").i ) + sprintf( defn+strlen(defn), " +R_a" ); + + if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_a").i ) + sprintf( defn+strlen(defn), " +R_lat_a=%s", + pj_param(pj_in->ctx,pj_in->params,"sR_lat_a").s ); + + if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_g").i ) + sprintf( defn+strlen(defn), " +R_lat_g=%s", + pj_param(pj_in->ctx,pj_in->params,"sR_lat_g").s ); + + /* copy over prime meridian */ + if( pj_param(pj_in->ctx,pj_in->params, "tpm").i ) + sprintf( defn+strlen(defn), " +pm=%s", + pj_param(pj_in->ctx,pj_in->params,"spm").s ); + + return pj_init_plus_ctx( pj_in->ctx, defn ); +} + +/************************************************************************/ +/* pj_get_spheroid_defn() */ +/* */ +/* Fetch the internal definition of the spheroid. Note that */ +/* you can compute "b" from eccentricity_squared as: */ +/* */ +/* b = a * sqrt(1 - es) */ +/************************************************************************/ + +void pj_get_spheroid_defn(projPJ defn, double *major_axis, double *eccentricity_squared) +{ + if ( major_axis ) + *major_axis = defn->a; + + if ( eccentricity_squared ) + *eccentricity_squared = defn->es; +} diff --git a/src/pj_zpoly1.c b/src/pj_zpoly1.c deleted file mode 100644 index bacb62ce..00000000 --- a/src/pj_zpoly1.c +++ /dev/null @@ -1,46 +0,0 @@ -/* evaluate complex polynomial */ -#include "projects.h" -/* note: coefficients are always from C_1 to C_n -** i.e. C_0 == (0., 0) -** n should always be >= 1 though no checks are made -*/ - COMPLEX -pj_zpoly1(COMPLEX z, const COMPLEX *C, int n) { - COMPLEX a; - double t; - - a = *(C += n); - while (n-- > 0) { - a.r = (--C)->r + z.r * (t = a.r) - z.i * a.i; - a.i = C->i + z.r * a.i + z.i * t; - } - a.r = z.r * (t = a.r) - z.i * a.i; - a.i = z.r * a.i + z.i * t; - return a; -} -/* evaluate complex polynomial and derivative */ - COMPLEX -pj_zpolyd1(COMPLEX z, const COMPLEX *C, int n, COMPLEX *der) { - COMPLEX a, b; - double t; - int first = 1; - - a = *(C += n); - b = a; - while (n-- > 0) { - if (first) { - first = 0; - } else { - b.r = a.r + z.r * (t = b.r) - z.i * b.i; - b.i = a.i + z.r * b.i + z.i * t; - } - a.r = (--C)->r + z.r * (t = a.r) - z.i * a.i; - a.i = C->i + z.r * a.i + z.i * t; - } - b.r = a.r + z.r * (t = b.r) - z.i * b.i; - b.i = a.i + z.r * b.i + z.i * t; - a.r = z.r * (t = a.r) - z.i * a.i; - a.i = z.r * a.i + z.i * t; - *der = b; - return a; -} diff --git a/src/pj_zpoly1.cpp b/src/pj_zpoly1.cpp new file mode 100644 index 00000000..bacb62ce --- /dev/null +++ b/src/pj_zpoly1.cpp @@ -0,0 +1,46 @@ +/* evaluate complex polynomial */ +#include "projects.h" +/* note: coefficients are always from C_1 to C_n +** i.e. C_0 == (0., 0) +** n should always be >= 1 though no checks are made +*/ + COMPLEX +pj_zpoly1(COMPLEX z, const COMPLEX *C, int n) { + COMPLEX a; + double t; + + a = *(C += n); + while (n-- > 0) { + a.r = (--C)->r + z.r * (t = a.r) - z.i * a.i; + a.i = C->i + z.r * a.i + z.i * t; + } + a.r = z.r * (t = a.r) - z.i * a.i; + a.i = z.r * a.i + z.i * t; + return a; +} +/* evaluate complex polynomial and derivative */ + COMPLEX +pj_zpolyd1(COMPLEX z, const COMPLEX *C, int n, COMPLEX *der) { + COMPLEX a, b; + double t; + int first = 1; + + a = *(C += n); + b = a; + while (n-- > 0) { + if (first) { + first = 0; + } else { + b.r = a.r + z.r * (t = b.r) - z.i * b.i; + b.i = a.i + z.r * b.i + z.i * t; + } + a.r = (--C)->r + z.r * (t = a.r) - z.i * a.i; + a.i = C->i + z.r * a.i + z.i * t; + } + b.r = a.r + z.r * (t = b.r) - z.i * b.i; + b.i = a.i + z.r * b.i + z.i * t; + a.r = z.r * (t = a.r) - z.i * a.i; + a.i = z.r * a.i + z.i * t; + *der = b; + return a; +} diff --git a/src/proj.c b/src/proj.c deleted file mode 100644 index 2405781c..00000000 --- a/src/proj.c +++ /dev/null @@ -1,581 +0,0 @@ -/* <<<< Cartographic projection filter program >>>> */ -#include "proj.h" -#include "projects.h" -#include -#include -#include -#include -#include -#include "emess.h" - -#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__) -# include -# include -# define SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) -#else -# define SET_BINARY_MODE(file) -#endif - -#define MAX_LINE 1000 -#define MAX_PARGS 100 -#define PJ_INVERS(P) (P->inv ? 1 : 0) - -extern void gen_cheb(int, projUV(*)(projUV), char *, PJ *, int, char **); - -static PJ *Proj; -static union { - projUV (*generic)(projUV, PJ *); - projXY (*fwd)(projLP, PJ *); - projLP (*inv)(projXY, PJ *); -} proj; - -static int - reversein = 0, /* != 0 reverse input arguments */ - reverseout = 0, /* != 0 reverse output arguments */ - bin_in = 0, /* != 0 then binary input */ - bin_out = 0, /* != 0 then binary output */ - echoin = 0, /* echo input data to output line */ - tag = '#', /* beginning of line tag character */ - inverse = 0, /* != 0 then inverse projection */ - prescale = 0, /* != 0 apply cartesian scale factor */ - dofactors = 0, /* determine scale factors */ - very_verby = 0, /* very verbose mode */ - postscale = 0; - -static char - *cheby_str, /* string controlling Chebychev evaluation */ - *oform = (char *)0, /* output format for x-y or decimal degrees */ - oform_buffer[16]; /* Buffer for oform when using -d */ - -static const char - *oterr = "*\t*", /* output line for unprojectable input */ - *usage = "%s\nusage: %s [ -bdeEfiIlmorsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n"; - -static PJ_FACTORS facs; - -static double (*informat)(const char *, char **), /* input data deformatter function */ - fscale = 0.; /* cartesian scale factor */ - -static projUV int_proj(projUV data) { - if (prescale) { - data.u *= fscale; - data.v *= fscale; - } - - data = (*proj.generic)(data, Proj); - - if (postscale && data.u != HUGE_VAL) { - data.u *= fscale; - data.v *= fscale; - } - - return data; -} - -/* file processing function */ -static void process(FILE *fid) { - char line[MAX_LINE+3], *s = 0, pline[40]; - PJ_COORD data; - - for (;;) { - int facs_bad = 0; - ++emess_dat.File_line; - - if (bin_in) { /* binary input */ - if (fread(&data, sizeof(projUV), 1, fid) != 1) - break; - } else { /* ascii input */ - if (!(s = fgets(line, MAX_LINE, fid))) - break; - - if (!strchr(s, '\n')) { /* overlong line */ - int c; - (void)strcat(s, "\n"); - /* gobble up to newline */ - while ((c = fgetc(fid)) != EOF && c != '\n') ; - } - - if (*s == tag) { - if (!bin_out) - (void)fputs(line, stdout); - continue; - } - - if (reversein) { - data.uv.v = (*informat)(s, &s); - data.uv.u = (*informat)(s, &s); - } else { - data.uv.u = (*informat)(s, &s); - data.uv.v = (*informat)(s, &s); - } - - if (data.uv.v == HUGE_VAL) - data.uv.u = HUGE_VAL; - - if (!*s && (s > line)) --s; /* assumed we gobbled \n */ - if (!bin_out && echoin) { - char t; - t = *s; - *s = '\0'; - (void)fputs(line, stdout); - *s = t; - putchar('\t'); - } - } - - if (data.uv.u != HUGE_VAL) { - PJ_COORD coord; - coord.lp = data.lp; - if (prescale) { data.uv.u *= fscale; data.uv.v *= fscale; } - if (dofactors && !inverse) { - facs = proj_factors(Proj, coord); - facs_bad = proj_errno(Proj); - } - - data.xy = (*proj.fwd)(data.lp, Proj); - - if (dofactors && inverse) { - facs = proj_factors(Proj, coord); - facs_bad = proj_errno(Proj); - } - - if (postscale && data.uv.u != HUGE_VAL) - { data.uv.u *= fscale; data.uv.v *= fscale; } - } - - if (bin_out) { /* binary output */ - (void)fwrite(&data, sizeof(projUV), 1, stdout); - continue; - } else if (data.uv.u == HUGE_VAL) /* error output */ - (void)fputs(oterr, stdout); - else if (inverse && !oform) { /*ascii DMS output */ - if (reverseout) { - (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); - putchar('\t'); - (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); - } else { - (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); - putchar('\t'); - (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); - } - } else { /* x-y or decimal degree ascii output, scale if warranted by output units */ - if (inverse) { - if (proj_angular_input(Proj, PJ_FWD)) { - data.uv.v *= RAD_TO_DEG; - data.uv.u *= RAD_TO_DEG; - } - } else { - if (proj_angular_output(Proj, PJ_FWD)) { - data.uv.v *= RAD_TO_DEG; - data.uv.u *= RAD_TO_DEG; - } - } - - if (reverseout) { - (void)printf(oform, data.uv.v); putchar('\t'); - (void)printf(oform, data.uv.u); - } else { - (void)printf(oform, data.uv.u); putchar('\t'); - (void)printf(oform, data.uv.v); - } - } - - /* print scale factor data */ - if (dofactors) { - if (!facs_bad) - (void)printf("\t<%g %g %g %g %g %g>", - facs.meridional_scale, facs.parallel_scale, facs.areal_scale, - facs.angular_distortion * RAD_TO_DEG, facs.tissot_semimajor, facs.tissot_semiminor); - else - (void)fputs("\t<* * * * * *>", stdout); - } - (void)fputs(bin_in ? "\n" : s, stdout); - } -} - -/* file processing function --- verbosely */ -static void vprocess(FILE *fid) { - char line[MAX_LINE+3], *s, pline[40]; - LP dat_ll; - projXY dat_xy; - int linvers; - PJ_COORD coord; - - - if (!oform) - oform = "%.3f"; - - if (bin_in || bin_out) - emess(1,"binary I/O not available in -V option"); - - for (;;) { - proj_errno_reset(Proj); - ++emess_dat.File_line; - - if (!(s = fgets(line, MAX_LINE, fid))) - break; - - if (!strchr(s, '\n')) { /* overlong line */ - int c; - (void)strcat(s, "\n"); - /* gobble up to newline */ - while ((c = fgetc(fid)) != EOF && c != '\n') ; - } - - if (*s == tag) { /* pass on data */ - (void)fputs(s, stdout); - continue; - } - - /* check to override default input mode */ - if (*s == 'I' || *s == 'i') { - linvers = 1; - ++s; - } else - linvers = inverse; - - if (linvers) { - if (!PJ_INVERS(Proj)) { - emess(-1,"inverse for this projection not avail.\n"); - continue; - } - dat_xy.x = strtod(s, &s); - dat_xy.y = strtod(s, &s); - if (dat_xy.x == HUGE_VAL || dat_xy.y == HUGE_VAL) { - emess(-1,"lon-lat input conversion failure\n"); - continue; - } - if (prescale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } - if (reversein) { - projXY temp = dat_xy; - dat_xy.x = temp.y; - dat_xy.y = temp.x; - } - dat_ll = pj_inv(dat_xy, Proj); - } else { - dat_ll.lam = proj_dmstor(s, &s); - dat_ll.phi = proj_dmstor(s, &s); - if (dat_ll.lam == HUGE_VAL || dat_ll.phi == HUGE_VAL) { - emess(-1,"lon-lat input conversion failure\n"); - continue; - } - if (reversein) { - LP temp = dat_ll; - dat_ll.lam = temp.phi; - dat_ll.phi = temp.lam; - } - dat_xy = pj_fwd(dat_ll, Proj); - if (postscale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } - } - - /* For some reason pj_errno does not work as expected in some */ - /* versions of Visual Studio, so using pj_get_errno_ref instead */ - if (*pj_get_errno_ref()) { - emess(-1, pj_strerrno(*pj_get_errno_ref())); - continue; - } - - if (!*s && (s > line)) --s; /* assumed we gobbled \n */ - coord.lp = dat_ll; - facs = proj_factors(Proj, coord); - if (proj_errno(Proj)) { - emess(-1,"failed to compute factors\n\n"); - continue; - } - - if (*s != '\n') - (void)fputs(s, stdout); - - (void)fputs("Longitude: ", stdout); - (void)fputs(proj_rtodms(pline, dat_ll.lam, 'E', 'W'), stdout); - (void)printf(" [ %.11g ]\n", dat_ll.lam * RAD_TO_DEG); - (void)fputs("Latitude: ", stdout); - (void)fputs(proj_rtodms(pline, dat_ll.phi, 'N', 'S'), stdout); - (void)printf(" [ %.11g ]\n", dat_ll.phi * RAD_TO_DEG); - (void)fputs("Easting (x): ", stdout); - (void)printf(oform, dat_xy.x); putchar('\n'); - (void)fputs("Northing (y): ", stdout); - (void)printf(oform, dat_xy.y); putchar('\n'); - (void)printf("Meridian scale (h) : %.8f ( %.4g %% error )\n", facs.meridional_scale, (facs.meridional_scale-1.)*100.); - (void)printf("Parallel scale (k) : %.8f ( %.4g %% error )\n", facs.parallel_scale, (facs.parallel_scale-1.)*100.); - (void)printf("Areal scale (s): %.8f ( %.4g %% error )\n", facs.areal_scale, (facs.areal_scale-1.)*100.); - (void)printf("Angular distortion (w): %.3f\n", facs.angular_distortion * RAD_TO_DEG); - (void)printf("Meridian/Parallel angle: %.5f\n", facs.meridian_parallel_angle * RAD_TO_DEG); - (void)printf("Convergence : "); - (void)fputs(proj_rtodms(pline, facs.meridian_convergence, 0, 0), stdout); - (void)printf(" [ %.8f ]\n", facs.meridian_convergence * RAD_TO_DEG); - (void)printf("Max-min (Tissot axis a-b) scale error: %.5f %.5f\n\n", facs.tissot_semimajor, facs.tissot_semiminor); - } -} - -int main(int argc, char **argv) { - char *arg, **eargv = argv, *pargv[MAX_PARGS], **iargv = argv; - FILE *fid; - int pargc = 0, iargc = argc, eargc = 0, mon = 0; - - if ( (emess_dat.Prog_name = strrchr(*argv,DIR_CHAR)) != NULL) - ++emess_dat.Prog_name; - else emess_dat.Prog_name = *argv; - inverse = ! strncmp(emess_dat.Prog_name, "inv", 3); - if (argc <= 1 ) { - (void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name); - exit (0); - } - - /* process run line arguments */ - while (--argc > 0) { /* collect run line arguments */ - if(**++argv == '-') for(arg = *argv;;) { - switch(*++arg) { - case '\0': /* position of "stdin" */ - if (arg[-1] == '-') eargv[eargc++] = "-"; - break; - case 'b': /* binary I/O */ - bin_in = bin_out = 1; - continue; - case 'v': /* monitor dump of initialization */ - mon = 1; - continue; - case 'i': /* input binary */ - bin_in = 1; - continue; - case 'o': /* output binary */ - bin_out = 1; - continue; - case 'I': /* alt. method to spec inverse */ - inverse = 1; - continue; - case 'E': /* echo ascii input to ascii output */ - echoin = 1; - continue; - case 'V': /* very verbose processing mode */ - very_verby = 1; - mon = 1; - continue; - case 'S': /* compute scale factors */ - dofactors = 1; - continue; - case 't': /* set col. one char */ - if (arg[1]) tag = *++arg; - else emess(1,"missing -t col. 1 tag"); - continue; - case 'l': /* list projections, ellipses or units */ - if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') { - /* list projections */ - const struct PJ_LIST *lp; - int do_long = arg[1] == 'P', c; - const char *str; - - for (lp = proj_list_operations() ; lp->id ; ++lp) { - if( strcmp(lp->id,"latlong") == 0 - || strcmp(lp->id,"longlat") == 0 - || strcmp(lp->id,"geocent") == 0 ) - continue; - - (void)printf("%s : ", lp->id); - if (do_long) /* possibly multiline description */ - (void)puts(*lp->descr); - else { /* first line, only */ - str = *lp->descr; - while ((c = *str++) && c != '\n') - putchar(c); - putchar('\n'); - } - } - } else if (arg[1] == '=') { /* list projection 'descr' */ - const struct PJ_LIST *lp; - - arg += 2; - for (lp = proj_list_operations(); lp->id ; ++lp) - if (!strcmp(lp->id, arg)) { - (void)printf("%9s : %s\n", lp->id, *lp->descr); - break; - } - } else if (arg[1] == 'e') { /* list ellipses */ - const struct PJ_ELLPS *le; - - for (le = proj_list_ellps(); le->id ; ++le) - (void)printf("%9s %-16s %-16s %s\n", - le->id, le->major, le->ell, le->name); - } else if (arg[1] == 'u') { /* list units */ - const struct PJ_UNITS *lu; - - for (lu = proj_list_units(); lu->id ; ++lu) - (void)printf("%12s %-20s %s\n", - lu->id, lu->to_meter, lu->name); - } else if (arg[1] == 'd') { /* list datums */ - const struct PJ_DATUMS *ld; - - printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" ); - for (ld = pj_get_datums_ref(); ld->id ; ++ld) - { - printf("%12s %-12s %-30s\n", - ld->id, ld->ellipse_id, ld->defn); - if( ld->comments != NULL && strlen(ld->comments) > 0 ) - printf( "%25s %s\n", " ", ld->comments ); - } - } else - emess(1,"invalid list option: l%c",arg[1]); - exit(0); - /* cppcheck-suppress duplicateBreak */ - continue; /* artificial */ - case 'e': /* error line alternative */ - if (--argc <= 0) - noargument: - emess(1,"missing argument for -%c",*arg); - oterr = *++argv; - continue; - case 'T': /* generate Chebyshev coefficients */ - if (--argc <= 0) goto noargument; - cheby_str = *++argv; - continue; - case 'm': /* cartesian multiplier */ - if (--argc <= 0) goto noargument; - postscale = 1; - if (!strncmp("1/",*++argv,2) || - !strncmp("1:",*argv,2)) { - if((fscale = atof((*argv)+2)) == 0.) - goto badscale; - fscale = 1. / fscale; - } else - if ((fscale = atof(*argv)) == 0.) { - badscale: - emess(1,"invalid scale argument"); - } - continue; - case 'W': /* specify seconds precision */ - case 'w': /* -W for constant field width */ - { - int c = arg[1]; - if (c != 0 && isdigit(c)) { - set_rtodms(c - '0', *arg == 'W'); - ++arg; - } else - emess(1,"-W argument missing or non-digit"); - continue; - } - case 'f': /* alternate output format degrees or xy */ - if (--argc <= 0) goto noargument; - oform = *++argv; - continue; - case 'd': - if (--argc <= 0) goto noargument; - sprintf(oform_buffer, "%%.%df", atoi(*++argv)); - oform = oform_buffer; - break; - case 'r': /* reverse input */ - reversein = 1; - continue; - case 's': /* reverse output */ - reverseout = 1; - continue; - default: - emess(1, "invalid option: -%c",*arg); - break; - } - break; - } else if (**argv == '+') { /* + argument */ - if (pargc < MAX_PARGS) - pargv[pargc++] = *argv + 1; - else - emess(1,"overflowed + argument table"); - } else /* assumed to be input file name(s) */ - eargv[eargc++] = *argv; - } - if (eargc == 0 && !cheby_str) /* if no specific files force sysin */ - eargv[eargc++] = "-"; - else if (eargc > 0 && cheby_str) /* warning */ - emess(4, "data files when generating Chebychev prohibited"); - /* done with parameter and control input */ - if (inverse && postscale) { - prescale = 1; - postscale = 0; - fscale = 1./fscale; - } - if (!(Proj = pj_init(pargc, pargv))) - emess(3,"projection initialization failure\ncause: %s", - pj_strerrno(pj_errno)); - - if (!proj_angular_input(Proj, PJ_FWD)) { - emess(3, "can't initialize operations that take non-angular input coordinates"); - exit(0); - } - - if (proj_angular_output(Proj, PJ_FWD)) { - emess(3, "can't initialize operations that produce angular output coordinates"); - exit(0); - } - - if (inverse) { - if (!Proj->inv) - emess(3,"inverse projection not available"); - proj.inv = pj_inv; - } else - proj.fwd = pj_fwd; - if (cheby_str) { - gen_cheb(inverse, int_proj, cheby_str, Proj, iargc, iargv); - exit(0); - } - - /* set input formatting control */ - if (mon) { - pj_pr_list(Proj); - if (very_verby) { - (void)printf("#Final Earth figure: "); - if (Proj->es != 0.0) { - (void)printf("ellipsoid\n# Major axis (a): "); - (void)printf(oform ? oform : "%.3f", Proj->a); - (void)printf("\n# 1/flattening: %.6f\n", - 1./(1. - sqrt(1. - Proj->es))); - (void)printf("# squared eccentricity: %.12f\n", Proj->es); - } else { - (void)printf("sphere\n# Radius: "); - (void)printf(oform ? oform : "%.3f", Proj->a); - (void)putchar('\n'); - } - } - } - - if (inverse) - informat = strtod; - else { - informat = proj_dmstor; - if (!oform) - oform = "%.2f"; - } - - if (bin_out) { - SET_BINARY_MODE(stdout); - } - - /* process input file list */ - for ( ; eargc-- ; ++eargv) { - if (**eargv == '-') { - fid = stdin; - emess_dat.File_name = ""; - - if (bin_in) - { - SET_BINARY_MODE(stdin); - } - - } else { - if ((fid = fopen(*eargv, "rb")) == NULL) { - emess(-2, *eargv, "input file"); - continue; - } - emess_dat.File_name = *eargv; - } - emess_dat.File_line = 0; - if (very_verby) - vprocess(fid); - else - process(fid); - (void)fclose(fid); - emess_dat.File_name = 0; - } - - if( Proj ) - pj_free(Proj); - - exit(0); /* normal completion */ -} diff --git a/src/proj.cpp b/src/proj.cpp new file mode 100644 index 00000000..2405781c --- /dev/null +++ b/src/proj.cpp @@ -0,0 +1,581 @@ +/* <<<< Cartographic projection filter program >>>> */ +#include "proj.h" +#include "projects.h" +#include +#include +#include +#include +#include +#include "emess.h" + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__WIN32__) +# include +# include +# define SET_BINARY_MODE(file) _setmode(_fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#define MAX_LINE 1000 +#define MAX_PARGS 100 +#define PJ_INVERS(P) (P->inv ? 1 : 0) + +extern void gen_cheb(int, projUV(*)(projUV), char *, PJ *, int, char **); + +static PJ *Proj; +static union { + projUV (*generic)(projUV, PJ *); + projXY (*fwd)(projLP, PJ *); + projLP (*inv)(projXY, PJ *); +} proj; + +static int + reversein = 0, /* != 0 reverse input arguments */ + reverseout = 0, /* != 0 reverse output arguments */ + bin_in = 0, /* != 0 then binary input */ + bin_out = 0, /* != 0 then binary output */ + echoin = 0, /* echo input data to output line */ + tag = '#', /* beginning of line tag character */ + inverse = 0, /* != 0 then inverse projection */ + prescale = 0, /* != 0 apply cartesian scale factor */ + dofactors = 0, /* determine scale factors */ + very_verby = 0, /* very verbose mode */ + postscale = 0; + +static char + *cheby_str, /* string controlling Chebychev evaluation */ + *oform = (char *)0, /* output format for x-y or decimal degrees */ + oform_buffer[16]; /* Buffer for oform when using -d */ + +static const char + *oterr = "*\t*", /* output line for unprojectable input */ + *usage = "%s\nusage: %s [ -bdeEfiIlmorsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n"; + +static PJ_FACTORS facs; + +static double (*informat)(const char *, char **), /* input data deformatter function */ + fscale = 0.; /* cartesian scale factor */ + +static projUV int_proj(projUV data) { + if (prescale) { + data.u *= fscale; + data.v *= fscale; + } + + data = (*proj.generic)(data, Proj); + + if (postscale && data.u != HUGE_VAL) { + data.u *= fscale; + data.v *= fscale; + } + + return data; +} + +/* file processing function */ +static void process(FILE *fid) { + char line[MAX_LINE+3], *s = 0, pline[40]; + PJ_COORD data; + + for (;;) { + int facs_bad = 0; + ++emess_dat.File_line; + + if (bin_in) { /* binary input */ + if (fread(&data, sizeof(projUV), 1, fid) != 1) + break; + } else { /* ascii input */ + if (!(s = fgets(line, MAX_LINE, fid))) + break; + + if (!strchr(s, '\n')) { /* overlong line */ + int c; + (void)strcat(s, "\n"); + /* gobble up to newline */ + while ((c = fgetc(fid)) != EOF && c != '\n') ; + } + + if (*s == tag) { + if (!bin_out) + (void)fputs(line, stdout); + continue; + } + + if (reversein) { + data.uv.v = (*informat)(s, &s); + data.uv.u = (*informat)(s, &s); + } else { + data.uv.u = (*informat)(s, &s); + data.uv.v = (*informat)(s, &s); + } + + if (data.uv.v == HUGE_VAL) + data.uv.u = HUGE_VAL; + + if (!*s && (s > line)) --s; /* assumed we gobbled \n */ + if (!bin_out && echoin) { + char t; + t = *s; + *s = '\0'; + (void)fputs(line, stdout); + *s = t; + putchar('\t'); + } + } + + if (data.uv.u != HUGE_VAL) { + PJ_COORD coord; + coord.lp = data.lp; + if (prescale) { data.uv.u *= fscale; data.uv.v *= fscale; } + if (dofactors && !inverse) { + facs = proj_factors(Proj, coord); + facs_bad = proj_errno(Proj); + } + + data.xy = (*proj.fwd)(data.lp, Proj); + + if (dofactors && inverse) { + facs = proj_factors(Proj, coord); + facs_bad = proj_errno(Proj); + } + + if (postscale && data.uv.u != HUGE_VAL) + { data.uv.u *= fscale; data.uv.v *= fscale; } + } + + if (bin_out) { /* binary output */ + (void)fwrite(&data, sizeof(projUV), 1, stdout); + continue; + } else if (data.uv.u == HUGE_VAL) /* error output */ + (void)fputs(oterr, stdout); + else if (inverse && !oform) { /*ascii DMS output */ + if (reverseout) { + (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); + putchar('\t'); + (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); + } else { + (void)fputs(rtodms(pline, data.uv.u, 'E', 'W'), stdout); + putchar('\t'); + (void)fputs(rtodms(pline, data.uv.v, 'N', 'S'), stdout); + } + } else { /* x-y or decimal degree ascii output, scale if warranted by output units */ + if (inverse) { + if (proj_angular_input(Proj, PJ_FWD)) { + data.uv.v *= RAD_TO_DEG; + data.uv.u *= RAD_TO_DEG; + } + } else { + if (proj_angular_output(Proj, PJ_FWD)) { + data.uv.v *= RAD_TO_DEG; + data.uv.u *= RAD_TO_DEG; + } + } + + if (reverseout) { + (void)printf(oform, data.uv.v); putchar('\t'); + (void)printf(oform, data.uv.u); + } else { + (void)printf(oform, data.uv.u); putchar('\t'); + (void)printf(oform, data.uv.v); + } + } + + /* print scale factor data */ + if (dofactors) { + if (!facs_bad) + (void)printf("\t<%g %g %g %g %g %g>", + facs.meridional_scale, facs.parallel_scale, facs.areal_scale, + facs.angular_distortion * RAD_TO_DEG, facs.tissot_semimajor, facs.tissot_semiminor); + else + (void)fputs("\t<* * * * * *>", stdout); + } + (void)fputs(bin_in ? "\n" : s, stdout); + } +} + +/* file processing function --- verbosely */ +static void vprocess(FILE *fid) { + char line[MAX_LINE+3], *s, pline[40]; + LP dat_ll; + projXY dat_xy; + int linvers; + PJ_COORD coord; + + + if (!oform) + oform = "%.3f"; + + if (bin_in || bin_out) + emess(1,"binary I/O not available in -V option"); + + for (;;) { + proj_errno_reset(Proj); + ++emess_dat.File_line; + + if (!(s = fgets(line, MAX_LINE, fid))) + break; + + if (!strchr(s, '\n')) { /* overlong line */ + int c; + (void)strcat(s, "\n"); + /* gobble up to newline */ + while ((c = fgetc(fid)) != EOF && c != '\n') ; + } + + if (*s == tag) { /* pass on data */ + (void)fputs(s, stdout); + continue; + } + + /* check to override default input mode */ + if (*s == 'I' || *s == 'i') { + linvers = 1; + ++s; + } else + linvers = inverse; + + if (linvers) { + if (!PJ_INVERS(Proj)) { + emess(-1,"inverse for this projection not avail.\n"); + continue; + } + dat_xy.x = strtod(s, &s); + dat_xy.y = strtod(s, &s); + if (dat_xy.x == HUGE_VAL || dat_xy.y == HUGE_VAL) { + emess(-1,"lon-lat input conversion failure\n"); + continue; + } + if (prescale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } + if (reversein) { + projXY temp = dat_xy; + dat_xy.x = temp.y; + dat_xy.y = temp.x; + } + dat_ll = pj_inv(dat_xy, Proj); + } else { + dat_ll.lam = proj_dmstor(s, &s); + dat_ll.phi = proj_dmstor(s, &s); + if (dat_ll.lam == HUGE_VAL || dat_ll.phi == HUGE_VAL) { + emess(-1,"lon-lat input conversion failure\n"); + continue; + } + if (reversein) { + LP temp = dat_ll; + dat_ll.lam = temp.phi; + dat_ll.phi = temp.lam; + } + dat_xy = pj_fwd(dat_ll, Proj); + if (postscale) { dat_xy.x *= fscale; dat_xy.y *= fscale; } + } + + /* For some reason pj_errno does not work as expected in some */ + /* versions of Visual Studio, so using pj_get_errno_ref instead */ + if (*pj_get_errno_ref()) { + emess(-1, pj_strerrno(*pj_get_errno_ref())); + continue; + } + + if (!*s && (s > line)) --s; /* assumed we gobbled \n */ + coord.lp = dat_ll; + facs = proj_factors(Proj, coord); + if (proj_errno(Proj)) { + emess(-1,"failed to compute factors\n\n"); + continue; + } + + if (*s != '\n') + (void)fputs(s, stdout); + + (void)fputs("Longitude: ", stdout); + (void)fputs(proj_rtodms(pline, dat_ll.lam, 'E', 'W'), stdout); + (void)printf(" [ %.11g ]\n", dat_ll.lam * RAD_TO_DEG); + (void)fputs("Latitude: ", stdout); + (void)fputs(proj_rtodms(pline, dat_ll.phi, 'N', 'S'), stdout); + (void)printf(" [ %.11g ]\n", dat_ll.phi * RAD_TO_DEG); + (void)fputs("Easting (x): ", stdout); + (void)printf(oform, dat_xy.x); putchar('\n'); + (void)fputs("Northing (y): ", stdout); + (void)printf(oform, dat_xy.y); putchar('\n'); + (void)printf("Meridian scale (h) : %.8f ( %.4g %% error )\n", facs.meridional_scale, (facs.meridional_scale-1.)*100.); + (void)printf("Parallel scale (k) : %.8f ( %.4g %% error )\n", facs.parallel_scale, (facs.parallel_scale-1.)*100.); + (void)printf("Areal scale (s): %.8f ( %.4g %% error )\n", facs.areal_scale, (facs.areal_scale-1.)*100.); + (void)printf("Angular distortion (w): %.3f\n", facs.angular_distortion * RAD_TO_DEG); + (void)printf("Meridian/Parallel angle: %.5f\n", facs.meridian_parallel_angle * RAD_TO_DEG); + (void)printf("Convergence : "); + (void)fputs(proj_rtodms(pline, facs.meridian_convergence, 0, 0), stdout); + (void)printf(" [ %.8f ]\n", facs.meridian_convergence * RAD_TO_DEG); + (void)printf("Max-min (Tissot axis a-b) scale error: %.5f %.5f\n\n", facs.tissot_semimajor, facs.tissot_semiminor); + } +} + +int main(int argc, char **argv) { + char *arg, **eargv = argv, *pargv[MAX_PARGS], **iargv = argv; + FILE *fid; + int pargc = 0, iargc = argc, eargc = 0, mon = 0; + + if ( (emess_dat.Prog_name = strrchr(*argv,DIR_CHAR)) != NULL) + ++emess_dat.Prog_name; + else emess_dat.Prog_name = *argv; + inverse = ! strncmp(emess_dat.Prog_name, "inv", 3); + if (argc <= 1 ) { + (void)fprintf(stderr, usage, pj_get_release(), emess_dat.Prog_name); + exit (0); + } + + /* process run line arguments */ + while (--argc > 0) { /* collect run line arguments */ + if(**++argv == '-') for(arg = *argv;;) { + switch(*++arg) { + case '\0': /* position of "stdin" */ + if (arg[-1] == '-') eargv[eargc++] = "-"; + break; + case 'b': /* binary I/O */ + bin_in = bin_out = 1; + continue; + case 'v': /* monitor dump of initialization */ + mon = 1; + continue; + case 'i': /* input binary */ + bin_in = 1; + continue; + case 'o': /* output binary */ + bin_out = 1; + continue; + case 'I': /* alt. method to spec inverse */ + inverse = 1; + continue; + case 'E': /* echo ascii input to ascii output */ + echoin = 1; + continue; + case 'V': /* very verbose processing mode */ + very_verby = 1; + mon = 1; + continue; + case 'S': /* compute scale factors */ + dofactors = 1; + continue; + case 't': /* set col. one char */ + if (arg[1]) tag = *++arg; + else emess(1,"missing -t col. 1 tag"); + continue; + case 'l': /* list projections, ellipses or units */ + if (!arg[1] || arg[1] == 'p' || arg[1] == 'P') { + /* list projections */ + const struct PJ_LIST *lp; + int do_long = arg[1] == 'P', c; + const char *str; + + for (lp = proj_list_operations() ; lp->id ; ++lp) { + if( strcmp(lp->id,"latlong") == 0 + || strcmp(lp->id,"longlat") == 0 + || strcmp(lp->id,"geocent") == 0 ) + continue; + + (void)printf("%s : ", lp->id); + if (do_long) /* possibly multiline description */ + (void)puts(*lp->descr); + else { /* first line, only */ + str = *lp->descr; + while ((c = *str++) && c != '\n') + putchar(c); + putchar('\n'); + } + } + } else if (arg[1] == '=') { /* list projection 'descr' */ + const struct PJ_LIST *lp; + + arg += 2; + for (lp = proj_list_operations(); lp->id ; ++lp) + if (!strcmp(lp->id, arg)) { + (void)printf("%9s : %s\n", lp->id, *lp->descr); + break; + } + } else if (arg[1] == 'e') { /* list ellipses */ + const struct PJ_ELLPS *le; + + for (le = proj_list_ellps(); le->id ; ++le) + (void)printf("%9s %-16s %-16s %s\n", + le->id, le->major, le->ell, le->name); + } else if (arg[1] == 'u') { /* list units */ + const struct PJ_UNITS *lu; + + for (lu = proj_list_units(); lu->id ; ++lu) + (void)printf("%12s %-20s %s\n", + lu->id, lu->to_meter, lu->name); + } else if (arg[1] == 'd') { /* list datums */ + const struct PJ_DATUMS *ld; + + printf("__datum_id__ __ellipse___ __definition/comments______________________________\n" ); + for (ld = pj_get_datums_ref(); ld->id ; ++ld) + { + printf("%12s %-12s %-30s\n", + ld->id, ld->ellipse_id, ld->defn); + if( ld->comments != NULL && strlen(ld->comments) > 0 ) + printf( "%25s %s\n", " ", ld->comments ); + } + } else + emess(1,"invalid list option: l%c",arg[1]); + exit(0); + /* cppcheck-suppress duplicateBreak */ + continue; /* artificial */ + case 'e': /* error line alternative */ + if (--argc <= 0) + noargument: + emess(1,"missing argument for -%c",*arg); + oterr = *++argv; + continue; + case 'T': /* generate Chebyshev coefficients */ + if (--argc <= 0) goto noargument; + cheby_str = *++argv; + continue; + case 'm': /* cartesian multiplier */ + if (--argc <= 0) goto noargument; + postscale = 1; + if (!strncmp("1/",*++argv,2) || + !strncmp("1:",*argv,2)) { + if((fscale = atof((*argv)+2)) == 0.) + goto badscale; + fscale = 1. / fscale; + } else + if ((fscale = atof(*argv)) == 0.) { + badscale: + emess(1,"invalid scale argument"); + } + continue; + case 'W': /* specify seconds precision */ + case 'w': /* -W for constant field width */ + { + int c = arg[1]; + if (c != 0 && isdigit(c)) { + set_rtodms(c - '0', *arg == 'W'); + ++arg; + } else + emess(1,"-W argument missing or non-digit"); + continue; + } + case 'f': /* alternate output format degrees or xy */ + if (--argc <= 0) goto noargument; + oform = *++argv; + continue; + case 'd': + if (--argc <= 0) goto noargument; + sprintf(oform_buffer, "%%.%df", atoi(*++argv)); + oform = oform_buffer; + break; + case 'r': /* reverse input */ + reversein = 1; + continue; + case 's': /* reverse output */ + reverseout = 1; + continue; + default: + emess(1, "invalid option: -%c",*arg); + break; + } + break; + } else if (**argv == '+') { /* + argument */ + if (pargc < MAX_PARGS) + pargv[pargc++] = *argv + 1; + else + emess(1,"overflowed + argument table"); + } else /* assumed to be input file name(s) */ + eargv[eargc++] = *argv; + } + if (eargc == 0 && !cheby_str) /* if no specific files force sysin */ + eargv[eargc++] = "-"; + else if (eargc > 0 && cheby_str) /* warning */ + emess(4, "data files when generating Chebychev prohibited"); + /* done with parameter and control input */ + if (inverse && postscale) { + prescale = 1; + postscale = 0; + fscale = 1./fscale; + } + if (!(Proj = pj_init(pargc, pargv))) + emess(3,"projection initialization failure\ncause: %s", + pj_strerrno(pj_errno)); + + if (!proj_angular_input(Proj, PJ_FWD)) { + emess(3, "can't initialize operations that take non-angular input coordinates"); + exit(0); + } + + if (proj_angular_output(Proj, PJ_FWD)) { + emess(3, "can't initialize operations that produce angular output coordinates"); + exit(0); + } + + if (inverse) { + if (!Proj->inv) + emess(3,"inverse projection not available"); + proj.inv = pj_inv; + } else + proj.fwd = pj_fwd; + if (cheby_str) { + gen_cheb(inverse, int_proj, cheby_str, Proj, iargc, iargv); + exit(0); + } + + /* set input formatting control */ + if (mon) { + pj_pr_list(Proj); + if (very_verby) { + (void)printf("#Final Earth figure: "); + if (Proj->es != 0.0) { + (void)printf("ellipsoid\n# Major axis (a): "); + (void)printf(oform ? oform : "%.3f", Proj->a); + (void)printf("\n# 1/flattening: %.6f\n", + 1./(1. - sqrt(1. - Proj->es))); + (void)printf("# squared eccentricity: %.12f\n", Proj->es); + } else { + (void)printf("sphere\n# Radius: "); + (void)printf(oform ? oform : "%.3f", Proj->a); + (void)putchar('\n'); + } + } + } + + if (inverse) + informat = strtod; + else { + informat = proj_dmstor; + if (!oform) + oform = "%.2f"; + } + + if (bin_out) { + SET_BINARY_MODE(stdout); + } + + /* process input file list */ + for ( ; eargc-- ; ++eargv) { + if (**eargv == '-') { + fid = stdin; + emess_dat.File_name = ""; + + if (bin_in) + { + SET_BINARY_MODE(stdin); + } + + } else { + if ((fid = fopen(*eargv, "rb")) == NULL) { + emess(-2, *eargv, "input file"); + continue; + } + emess_dat.File_name = *eargv; + } + emess_dat.File_line = 0; + if (very_verby) + vprocess(fid); + else + process(fid); + (void)fclose(fid); + emess_dat.File_name = 0; + } + + if( Proj ) + pj_free(Proj); + + exit(0); /* normal completion */ +} diff --git a/src/proj_4D_api.c b/src/proj_4D_api.c deleted file mode 100644 index 75a061a0..00000000 --- a/src/proj_4D_api.c +++ /dev/null @@ -1,1281 +0,0 @@ -/****************************************************************************** - * Project: PROJ.4 - * Purpose: Implement a (currently minimalistic) proj API based primarily - * on the PJ_COORD 4D geodetic spatiotemporal data type. - * - * Author: Thomas Knudsen, thokn@sdfe.dk, 2016-06-09/2016-11-06 - * - ****************************************************************************** - * Copyright (c) 2016, 2017 Thomas Knudsen/SDFE - * - * 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 -#include -#include -#include -#include -#ifndef _MSC_VER -#include -#endif - -#include "proj.h" -#include "proj_internal.h" -#include "proj_math.h" -#include "projects.h" -#include "geodesic.h" - - -/* Initialize PJ_COORD struct */ -PJ_COORD proj_coord (double x, double y, double z, double t) { - PJ_COORD res; - res.v[0] = x; - res.v[1] = y; - res.v[2] = z; - res.v[3] = t; - return res; -} - -/*****************************************************************************/ -int proj_angular_input (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P expects angular input coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - if (PJ_FWD==dir) - return pj_left (P)==PJ_IO_UNITS_ANGULAR; - return pj_right (P)==PJ_IO_UNITS_ANGULAR; -} - -/*****************************************************************************/ -int proj_angular_output (PJ *P, enum PJ_DIRECTION dir) { -/****************************************************************************** - Returns 1 if the operator P provides angular output coordinates when - operating in direction dir, 0 otherwise. - dir: {PJ_FWD, PJ_INV} -******************************************************************************/ - return proj_angular_input (P, -dir); -} - - -/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the ellipsoid */ -PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b) { - PJ_COORD c; - /* Note: the geodesic code takes arguments in degrees */ - geod_inverse (P->geod, - PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), - PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), - c.v, c.v+1, c.v+2 - ); - - return c; -} - - -/* Geodesic distance (in meter) between two points with angular 2D coordinates */ -double proj_lp_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { - double s12, azi1, azi2; - /* Note: the geodesic code takes arguments in degrees */ - geod_inverse (P->geod, - PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), - PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), - &s12, &azi1, &azi2 - ); - return s12; -} - -/* The geodesic distance AND the vertical offset */ -double proj_lpz_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { - if (HUGE_VAL==a.lpz.lam || HUGE_VAL==b.lpz.lam) - return HUGE_VAL; - return hypot (proj_lp_dist (P, a, b), a.lpz.z - b.lpz.z); -} - -/* Euclidean distance between two points with linear 2D coordinates */ -double proj_xy_dist (PJ_COORD a, PJ_COORD b) { - return hypot (a.xy.x - b.xy.x, a.xy.y - b.xy.y); -} - -/* Euclidean distance between two points with linear 3D coordinates */ -double proj_xyz_dist (PJ_COORD a, PJ_COORD b) { - return hypot (proj_xy_dist (a, b), a.xyz.z - b.xyz.z); -} - - - -/* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ -double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) { - int i; - PJ_COORD t, org; - - if (0==P) - return HUGE_VAL; - - if (n < 1) { - proj_errno_set (P, EINVAL); - return HUGE_VAL; - } - - /* in the first half-step, we generate the output value */ - org = *coord; - *coord = proj_trans (P, direction, org); - t = *coord; - - /* now we take n-1 full steps in inverse direction: We are */ - /* out of phase due to the half step already taken */ - for (i = 0; i < n - 1; i++) - t = proj_trans (P, direction, proj_trans (P, -direction, t) ); - - /* finally, we take the last half-step */ - t = proj_trans (P, -direction, t); - - /* checking for angular *input* since we do a roundtrip, and end where we begin */ - if (proj_angular_input (P, direction)) - return proj_lpz_dist (P, org, t); - - return proj_xyz_dist (org, t); -} - - - -/**************************************************************************************/ -PJ_COORD proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { -/*************************************************************************************** -Apply the transformation P to the coordinate coord, preferring the 4D interfaces if -available. - -See also pj_approx_2D_trans and pj_approx_3D_trans in pj_internal.c, which work -similarly, but prefers the 2D resp. 3D interfaces if available. -***************************************************************************************/ - if (0==P) - return coord; - if (P->inverted) - direction = -direction; - - switch (direction) { - case PJ_FWD: - return pj_fwd4d (coord, P); - case PJ_INV: - return pj_inv4d (coord, P); - case PJ_IDENT: - return coord; - default: - break; - } - - proj_errno_set (P, EINVAL); - return proj_coord_error (); -} - - - -/*****************************************************************************/ -int proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) { -/****************************************************************************** - Batch transform an array of PJ_COORD. - - Returns 0 if all coordinates are transformed without error, otherwise - returns error number. -******************************************************************************/ - size_t i; - - for (i = 0; i < n; i++) { - coord[i] = proj_trans (P, direction, coord[i]); - if (proj_errno(P)) - return proj_errno (P); - } - - return 0; -} - - - -/*************************************************************************************/ -size_t proj_trans_generic ( - PJ *P, - PJ_DIRECTION direction, - double *x, size_t sx, size_t nx, - double *y, size_t sy, size_t ny, - double *z, size_t sz, size_t nz, - double *t, size_t st, size_t nt -) { -/************************************************************************************** - - Transform a series of coordinates, where the individual coordinate dimension - may be represented by an array that is either - - 1. fully populated - 2. a null pointer and/or a length of zero, which will be treated as a - fully populated array of zeroes - 3. of length one, i.e. a constant, which will be treated as a fully - populated array of that constant value - - The strides, sx, sy, sz, st, represent the step length, in bytes, between - consecutive elements of the corresponding array. This makes it possible for - proj_transform to handle transformation of a large class of application - specific data structures, without necessarily understanding the data structure - format, as in: - - typedef struct {double x, y; int quality_level; char surveyor_name[134];} XYQS; - XYQS survey[345]; - double height = 23.45; - PJ *P = {...}; - size_t stride = sizeof (XYQS); - ... - proj_transform ( - P, PJ_INV, sizeof(XYQS), - &(survey[0].x), stride, 345, (* We have 345 eastings *) - &(survey[0].y), stride, 345, (* ...and 345 northings. *) - &height, 1, (* The height is the constant 23.45 m *) - 0, 0 (* and the time is the constant 0.00 s *) - ); - - This is similar to the inner workings of the pj_transform function, but the - stride functionality has been generalized to work for any size of basic unit, - not just a fixed number of doubles. - - In most cases, the stride will be identical for x, y,z, and t, since they will - typically be either individual arrays (stride = sizeof(double)), or strided - views into an array of application specific data structures (stride = sizeof (...)). - - But in order to support cases where x, y, z, and t come from heterogeneous - sources, individual strides, sx, sy, sz, st, are used. - - Caveat: Since proj_transform does its work *in place*, this means that even the - supposedly constants (i.e. length 1 arrays) will return from the call in altered - state. Hence, remember to reinitialize between repeated calls. - - Return value: Number of transformations completed. - -**************************************************************************************/ - PJ_COORD coord = {{0,0,0,0}}; - size_t i, nmin; - double null_broadcast = 0; - - if (0==P) - return 0; - - if (P->inverted) - direction = -direction; - - /* ignore lengths of null arrays */ - if (0==x) nx = 0; - if (0==y) ny = 0; - if (0==z) nz = 0; - if (0==t) nt = 0; - - /* and make the nullities point to some real world memory for broadcasting nulls */ - if (0==nx) x = &null_broadcast; - if (0==ny) y = &null_broadcast; - if (0==nz) z = &null_broadcast; - if (0==nt) t = &null_broadcast; - - /* nothing to do? */ - if (0==nx+ny+nz+nt) - return 0; - - /* arrays of length 1 are constants, which we broadcast along the longer arrays */ - /* so we need to find the length of the shortest non-unity array to figure out */ - /* how many coordinate pairs we must transform */ - nmin = (nx > 1)? nx: (ny > 1)? ny: (nz > 1)? nz: (nt > 1)? nt: 1; - if ((nx > 1) && (nx < nmin)) nmin = nx; - if ((ny > 1) && (ny < nmin)) nmin = ny; - if ((nz > 1) && (nz < nmin)) nmin = nz; - if ((nt > 1) && (nt < nmin)) nmin = nt; - - /* Check validity of direction flag */ - switch (direction) { - case PJ_FWD: - case PJ_INV: - break; - case PJ_IDENT: - return nmin; - default: - proj_errno_set (P, EINVAL); - return 0; - } - - /* Arrays of length==0 are broadcast as the constant 0 */ - /* Arrays of length==1 are broadcast as their single value */ - /* Arrays of length >1 are iterated over (for the first nmin values) */ - /* The slightly convolved incremental indexing is used due */ - /* to the stride, which may be any size supported by the platform */ - for (i = 0; i < nmin; i++) { - coord.xyzt.x = *x; - coord.xyzt.y = *y; - coord.xyzt.z = *z; - coord.xyzt.t = *t; - - if (PJ_FWD==direction) - coord = pj_fwd4d (coord, P); - else - coord = pj_inv4d (coord, P); - - /* in all full length cases, we overwrite the input with the output, */ - /* and step on to the next element. */ - /* The casts are somewhat funky, but they compile down to no-ops and */ - /* they tell compilers and static analyzers that we know what we do */ - if (nx > 1) { - *x = coord.xyzt.x; - x = (double *) ((void *) ( ((char *) x) + sx)); - } - if (ny > 1) { - *y = coord.xyzt.y; - y = (double *) ((void *) ( ((char *) y) + sy)); - } - if (nz > 1) { - *z = coord.xyzt.z; - z = (double *) ((void *) ( ((char *) z) + sz)); - } - if (nt > 1) { - *t = coord.xyzt.t; - t = (double *) ((void *) ( ((char *) t) + st)); - } - } - - /* Last time around, we update the length 1 cases with their transformed alter egos */ - if (nx==1) - *x = coord.xyzt.x; - if (ny==1) - *y = coord.xyzt.y; - if (nz==1) - *z = coord.xyzt.z; - if (nt==1) - *t = coord.xyzt.t; - - return i; -} - - -/*************************************************************************************/ -PJ_COORD pj_geocentric_latitude (const PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { -/************************************************************************************** - Convert geographical latitude to geocentric (or the other way round if - direction = PJ_INV) - - The conversion involves a call to the tangent function, which goes through the - roof at the poles, so very close (the last centimeter) to the poles no - conversion takes place and the input latitude is copied directly to the output. - - Fortunately, the geocentric latitude converges to the geographical at the - poles, so the difference is negligible. - - For the spherical case, the geographical latitude equals the geocentric, and - consequently, the input is copied directly to the output. -**************************************************************************************/ - const double limit = M_HALFPI - 1e-9; - PJ_COORD res = coord; - if ((coord.lp.phi > limit) || (coord.lp.phi < -limit) || (P->es==0)) - return res; - if (direction==PJ_FWD) - res.lp.phi = atan (P->one_es * tan (coord.lp.phi) ); - else - res.lp.phi = atan (P->rone_es * tan (coord.lp.phi) ); - - return res; -} - -double proj_torad (double angle_in_degrees) { return PJ_TORAD (angle_in_degrees);} -double proj_todeg (double angle_in_radians) { return PJ_TODEG (angle_in_radians);} - -double proj_dmstor(const char *is, char **rs) { - return dmstor(is, rs); -} - -char* proj_rtodms(char *s, double r, int pos, int neg) { - return rtodms(s, r, pos, neg); -} - -/*************************************************************************************/ -static PJ* skip_prep_fin(PJ *P) { -/************************************************************************************** -Skip prepare and finalize function for the various "helper operations" added to P when -in cs2cs compatibility mode. -**************************************************************************************/ - P->skip_fwd_prepare = 1; - P->skip_fwd_finalize = 1; - P->skip_inv_prepare = 1; - P->skip_inv_finalize = 1; - return P; -} - -/*************************************************************************************/ -static int cs2cs_emulation_setup (PJ *P) { -/************************************************************************************** -If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the 4D API -equivalent operations, so the preparation and finalization steps in the pj_inv/pj_fwd -invocators can emulate the behaviour of pj_transform and the cs2cs app. - -Returns 1 on success, 0 on failure -**************************************************************************************/ - PJ *Q; - paralist *p; - int do_cart = 0; - if (0==P) - return 0; - - /* Don't recurse when calling proj_create (which calls us back) */ - if (pj_param_exists (P->params, "break_cs2cs_recursion")) - return 1; - - /* Swap axes? */ - p = pj_param_exists (P->params, "axis"); - - /* Don't axisswap if data are already in "enu" order */ - if (p && (0!=strcmp ("enu", p->param))) { - char *def = malloc (100+strlen(P->axis)); - if (0==def) - return 0; - sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); - Q = proj_create (P->ctx, def); - free (def); - if (0==Q) - return 0; - P->axisswap = skip_prep_fin(Q); - } - - /* Geoid grid(s) given? */ - p = pj_param_exists (P->params, "geoidgrids"); - if (p && strlen (p->param) > strlen ("geoidgrids=")) { - char *gridnames = p->param + strlen ("geoidgrids="); - char *def = malloc (100+strlen(gridnames)); - if (0==def) - return 0; - sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); - Q = proj_create (P->ctx, def); - free (def); - if (0==Q) - return 0; - P->vgridshift = skip_prep_fin(Q); - } - - /* Datum shift grid(s) given? */ - p = pj_param_exists (P->params, "nadgrids"); - if (p && strlen (p->param) > strlen ("nadgrids=")) { - char *gridnames = p->param + strlen ("nadgrids="); - char *def = malloc (100+strlen(gridnames)); - if (0==def) - return 0; - sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); - Q = proj_create (P->ctx, def); - free (def); - if (0==Q) - return 0; - P->hgridshift = skip_prep_fin(Q); - } - - /* We ignore helmert if we have grid shift */ - p = P->hgridshift ? 0 : pj_param_exists (P->params, "towgs84"); - while (p) { - char *def; - char *s = p->param; - double *d = P->datum_params; - size_t n = strlen (s); - - /* We ignore null helmert shifts (common in auto-translated resource files, e.g. epsg) */ - if (0==d[0] && 0==d[1] && 0==d[2] && 0==d[3] && 0==d[4] && 0==d[5] && 0==d[6]) { - /* If the current ellipsoid is not WGS84, then make sure the */ - /* change in ellipsoid is still done. */ - if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { - do_cart = 1; - } - break; - } - - if (n <= 8) /* 8==strlen ("towgs84=") */ - return 0; - - def = malloc (100+n); - if (0==def) - return 0; - sprintf (def, "break_cs2cs_recursion proj=helmert exact %s convention=position_vector", s); - Q = proj_create (P->ctx, def); - free(def); - if (0==Q) - return 0; - pj_inherit_ellipsoid_def (P, Q); - P->helmert = skip_prep_fin (Q); - - break; - } - - /* We also need cartesian/geographical transformations if we are working in */ - /* geocentric/cartesian space or we need to do a Helmert transform. */ - if (P->is_geocent || P->helmert || do_cart) { - char def[150]; - sprintf (def, "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", P->a_orig, P->es_orig); - { - /* In case the current locale does not use dot but comma as decimal */ - /* separator, replace it with dot, so that proj_atof() behaves */ - /* correctly. */ - /* TODO later: use C++ ostringstream with imbue(std::locale::classic()) */ - /* to be locale unaware */ - char* next_pos; - for (next_pos = def; (next_pos = strchr (next_pos, ',')) != NULL; next_pos++) { - *next_pos = '.'; - } - } - Q = proj_create (P->ctx, def); - if (0==Q) - return 0; - P->cart = skip_prep_fin (Q); - - if (!P->is_geocent) { - sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); - Q = proj_create (P->ctx, def); - if (0==Q) - return 0; - P->cart_wgs84 = skip_prep_fin (Q); - } - } - - return 1; -} - - - -/*************************************************************************************/ -PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { -/************************************************************************************** - Create a new PJ object in the context ctx, using the given definition. If ctx==0, - the default context is used, if definition==0, or invalid, a null-pointer is - returned. The definition may use '+' as argument start indicator, as in - "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32". - - It may even use free formatting "proj = utm; zone =32 ellps= GRS80". - Note that the semicolon separator is allowed, but not required. -**************************************************************************************/ - PJ *P; - char *args, **argv; - size_t argc, n; - int ret; - int allow_init_epsg; - - if (0==ctx) - ctx = pj_get_default_ctx (); - - /* Make a copy that we can manipulate */ - n = strlen (definition); - args = (char *) malloc (n + 1); - if (0==args) { - proj_context_errno_set(ctx, ENOMEM); - return 0; - } - strcpy (args, definition); - - argc = pj_trim_argc (args); - if (argc==0) { - pj_dealloc (args); - proj_context_errno_set(ctx, PJD_ERR_NO_ARGS); - return 0; - } - - argv = pj_trim_argv (argc, args); - - /* ...and let pj_init_ctx do the hard work */ - /* New interface: forbid init=epsg:XXXX syntax by default */ - allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, FALSE); - P = pj_init_ctx_with_allow_init_epsg (ctx, (int) argc, argv, allow_init_epsg); - - pj_dealloc (argv); - pj_dealloc (args); - - /* Support cs2cs-style modifiers */ - ret = cs2cs_emulation_setup (P); - if (0==ret) - return proj_destroy (P); - - return P; -} - - - -/*************************************************************************************/ -PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv) { -/************************************************************************************** -Create a new PJ object in the context ctx, using the given definition argument -array argv. If ctx==0, the default context is used, if definition==0, or invalid, -a null-pointer is returned. The definition arguments may use '+' as argument start -indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", -"zone=32"}. -**************************************************************************************/ - PJ *P; - const char *c; - - if (0==ctx) - ctx = pj_get_default_ctx (); - if (0==argv) { - proj_context_errno_set(ctx, PJD_ERR_NO_ARGS); - return 0; - } - - /* We assume that free format is used, and build a full proj_create compatible string */ - c = pj_make_args (argc, argv); - if (0==c) { - proj_context_errno_set(ctx, ENOMEM); - return 0; - } - - P = proj_create (ctx, c); - - pj_dealloc ((char *) c); - return P; -} - -/** Create an area of use */ -PJ_AREA * proj_area_create(void) { - return pj_calloc(1, sizeof(PJ_AREA)); -} - -/** Assign a bounding box to an area of use. */ -void proj_area_set_bbox(PJ_AREA *area, - double west_lon_degree, - double south_lat_degree, - double east_lon_degree, - double north_lat_degree) { - area->bbox_set = TRUE; - area->west_lon_degree = west_lon_degree; - area->south_lat_degree = south_lat_degree; - area->east_lon_degree = east_lon_degree; - area->north_lat_degree = north_lat_degree; -} - -/** Free an area of use */ -void proj_area_destroy(PJ_AREA* area) { - pj_dealloc(area); -} - -/************************************************************************/ -/* proj_context_use_proj4_init_rules() */ -/************************************************************************/ - -void proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable) { - if( ctx == NULL ) { - ctx = pj_get_default_ctx(); - } - ctx->use_proj4_init_rules = enable; -} - -/************************************************************************/ -/* EQUAL() */ -/************************************************************************/ - -static int EQUAL(const char* a, const char* b) { -#ifdef _MSC_VER - return _stricmp(a, b) == 0; -#else - return strcasecmp(a, b) == 0; -#endif -} - -/************************************************************************/ -/* proj_context_get_use_proj4_init_rules() */ -/************************************************************************/ - -int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path) { - const char* val = getenv("PROJ_USE_PROJ4_INIT_RULES"); - - if( ctx == NULL ) { - ctx = pj_get_default_ctx(); - } - - if( val ) { - if( EQUAL(val, "yes") || EQUAL(val, "on") || EQUAL(val, "true") ) { - return TRUE; - } - if( EQUAL(val, "no") || EQUAL(val, "off") || EQUAL(val, "false") ) { - return FALSE; - } - pj_log(ctx, PJ_LOG_ERROR, "Invalid value for PROJ_USE_PROJ4_INIT_RULES"); - } - - if( ctx->use_proj4_init_rules >= 0 ) { - return ctx->use_proj4_init_rules; - } - return from_legacy_code_path; -} - - -/*****************************************************************************/ -PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char *target_crs, PJ_AREA *area) { -/****************************************************************************** - Create a transformation pipeline between two known coordinate reference - systems. - - source_crs and target_crs can be : - - a "AUTHORITY:CODE", like EPSG:25832. When using that syntax for a source - CRS, the created pipeline will expect that the values passed to proj_trans() - respect the axis order and axis unit of the official definition ( - so for example, for EPSG:4326, with latitude first and longitude next, - in degrees). Similarly, when using that syntax for a target CRS, output - values will be emitted according to the official definition of this CRS. - - a PROJ string, like "+proj=longlat +datum=WGS84". - When using that syntax, the axis order and unit for geographic CRS will - be longitude, latitude, and the unit degrees. - - more generally any string accepted by proj_obj_create_from_user_input() - - An "area of use" can be specified in area. When it is supplied, the more - accurate transformation between two given systems can be chosen. - - Example call: - - PJ *P = proj_create_crs_to_crs(0, "EPSG:25832", "EPSG:25833", NULL); - -******************************************************************************/ - PJ *P; - PJ_OBJ* src; - PJ_OBJ* dst; - PJ_OPERATION_FACTORY_CONTEXT* operation_ctx; - PJ_OBJ_LIST* op_list; - PJ_OBJ* op; - const char* proj_string; - const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", NULL }; - const char* const* optionsImportCRS = - proj_context_get_use_proj4_init_rules(ctx, FALSE) ? optionsProj4Mode : NULL; - - src = proj_obj_create_from_user_input(ctx, source_crs, optionsImportCRS); - if( !src ) { - return NULL; - } - - dst = proj_obj_create_from_user_input(ctx, target_crs, optionsImportCRS); - if( !dst ) { - proj_obj_destroy(src); - return NULL; - } - - operation_ctx = proj_create_operation_factory_context(ctx, NULL); - if( !operation_ctx ) { - proj_obj_destroy(src); - proj_obj_destroy(dst); - return NULL; - } - - if( area && area->bbox_set ) { - proj_operation_factory_context_set_area_of_interest( - ctx, - operation_ctx, - area->west_lon_degree, - area->south_lat_degree, - area->east_lon_degree, - area->north_lat_degree); - } - - proj_operation_factory_context_set_grid_availability_use( - ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); - - op_list = proj_obj_create_operations(ctx, src, dst, operation_ctx); - - proj_operation_factory_context_destroy(operation_ctx); - proj_obj_destroy(src); - proj_obj_destroy(dst); - - if( !op_list ) { - return NULL; - } - - if( proj_obj_list_get_count(op_list) == 0 ) { - proj_obj_list_destroy(op_list); - return NULL; - } - - op = proj_obj_list_get(ctx, op_list, 0); - proj_obj_list_destroy(op_list); - if( !op ) { - return NULL; - } - - proj_string = proj_obj_as_proj_string(ctx, op, PJ_PROJ_5, NULL); - if( !proj_string) { - proj_obj_destroy(op); - return NULL; - } - - if( proj_string[0] == '\0' ) { - /* Null transform ? */ - P = proj_create(ctx, "proj=affine"); - } else { - P = proj_create(ctx, proj_string); - } - - proj_obj_destroy(op); - - return P; -} - -PJ *proj_destroy (PJ *P) { - pj_free (P); - return 0; -} - -/*****************************************************************************/ -int proj_errno (const PJ *P) { -/****************************************************************************** - Read an error level from the context of a PJ. -******************************************************************************/ - return pj_ctx_get_errno (pj_get_ctx ((PJ *) P)); -} - -/*****************************************************************************/ -int proj_context_errno (PJ_CONTEXT *ctx) { -/****************************************************************************** - Read an error directly from a context, without going through a PJ - belonging to that context. -******************************************************************************/ - if (0==ctx) - ctx = pj_get_default_ctx(); - return pj_ctx_get_errno (ctx); -} - -/*****************************************************************************/ -int proj_errno_set (const PJ *P, int err) { -/****************************************************************************** - Set context-errno, bubble it up to the thread local errno, return err -******************************************************************************/ - /* Use proj_errno_reset to explicitly clear the error status */ - if (0==err) - return 0; - - /* For P==0 err goes to the default context */ - proj_context_errno_set (pj_get_ctx ((PJ *) P), err); - errno = err; - return err; -} - -/*****************************************************************************/ -int proj_errno_restore (const PJ *P, int err) { -/****************************************************************************** - Use proj_errno_restore when the current function succeeds, but the - error flag was set on entry, and stored/reset using proj_errno_reset - in order to monitor for new errors. - - See usage example under proj_errno_reset () -******************************************************************************/ - if (0==err) - return 0; - proj_errno_set (P, err); - return 0; -} - -/*****************************************************************************/ -int proj_errno_reset (const PJ *P) { -/****************************************************************************** - Clears errno in the context and thread local levels - through the low level pj_ctx interface. - - Returns the previous value of the errno, for convenient reset/restore - operations: - - int foo (PJ *P) { - // errno may be set on entry, but we need to reset it to be able to - // check for errors from "do_something_with_P(P)" - int last_errno = proj_errno_reset (P); - - // local failure - if (0==P) - return proj_errno_set (P, 42); - - // call to function that may fail - do_something_with_P (P); - - // failure in do_something_with_P? - keep latest error status - if (proj_errno(P)) - return proj_errno (P); - - // success - restore previous error status, return 0 - return proj_errno_restore (P, last_errno); - } -******************************************************************************/ - int last_errno; - last_errno = proj_errno (P); - - pj_ctx_set_errno (pj_get_ctx ((PJ *) P), 0); - errno = 0; - pj_errno = 0; - return last_errno; -} - - -/* Create a new context */ -PJ_CONTEXT *proj_context_create (void) { - return pj_ctx_alloc (); -} - - -PJ_CONTEXT *proj_context_destroy (PJ_CONTEXT *ctx) { - if (0==ctx) - return 0; - - /* Trying to free the default context is a no-op (since it is statically allocated) */ - if (pj_get_default_ctx ()==ctx) - return 0; - - pj_ctx_free (ctx); - return 0; -} - - - - - - -/*****************************************************************************/ -static char *path_append (char *buf, const char *app, size_t *buf_size) { -/****************************************************************************** - Helper for proj_info() below. Append app to buf, separated by a - semicolon. Also handle allocation of longer buffer if needed. - - Returns buffer and adjusts *buf_size through provided pointer arg. -******************************************************************************/ - char *p; - size_t len, applen = 0, buflen = 0; -#ifdef _WIN32 - char *delim = ";"; -#else - char *delim = ":"; -#endif - - /* Nothing to do? */ - if (0 == app) - return buf; - applen = strlen (app); - if (0 == applen) - return buf; - - /* Start checking whether buf is long enough */ - if (0 != buf) - buflen = strlen (buf); - len = buflen+applen+strlen (delim) + 1; - - /* "pj_realloc", so to speak */ - if (*buf_size < len) { - p = pj_calloc (2 * len, sizeof (char)); - if (0==p) { - pj_dealloc (buf); - return 0; - } - *buf_size = 2 * len; - if (buf != 0) - strcpy (p, buf); - pj_dealloc (buf); - buf = p; - } - - /* Only append a semicolon if something's already there */ - if (0 != buflen) - strcat (buf, ";"); - strcat (buf, app); - return buf; -} - -static const char *empty = {""}; -static char version[64] = {""}; -static PJ_INFO info = {0, 0, 0, 0, 0, 0, 0, 0}; -static volatile int info_initialized = 0; - -/*****************************************************************************/ -PJ_INFO proj_info (void) { -/****************************************************************************** - Basic info about the current instance of the PROJ.4 library. - - Returns PJ_INFO struct. -******************************************************************************/ - const char * const *paths; - size_t i, n; - - size_t buf_size = 0; - char *buf = 0; - - pj_acquire_lock (); - - if (0!=info_initialized) { - pj_release_lock (); - return info; - } - - info.major = PROJ_VERSION_MAJOR; - info.minor = PROJ_VERSION_MINOR; - info.patch = PROJ_VERSION_PATCH; - - /* This is a controlled environment, so no risk of sprintf buffer - overflow. A normal version string is xx.yy.zz which is 8 characters - long and there is room for 64 bytes in the version string. */ - sprintf (version, "%d.%d.%d", info.major, info.minor, info.patch); - - info.searchpath = empty; - info.version = version; - info.release = pj_get_release (); - - /* build search path string */ - buf = path_append (buf, getenv ("HOME"), &buf_size); - buf = path_append (buf, getenv ("PROJ_LIB"), &buf_size); - - paths = proj_get_searchpath (); - n = (size_t) proj_get_path_count (); - - for (i = 0; i < n; i++) - buf = path_append (buf, paths[i], &buf_size); - info.searchpath = buf ? buf : empty; - - info.paths = paths; - info.path_count = n; - - info_initialized = 1; - pj_release_lock (); - return info; -} - - -/*****************************************************************************/ -PJ_PROJ_INFO proj_pj_info(PJ *P) { -/****************************************************************************** - Basic info about a particular instance of a projection object. - - Returns PJ_PROJ_INFO struct. -******************************************************************************/ - PJ_PROJ_INFO pjinfo; - char *def; - - memset(&pjinfo, 0, sizeof(PJ_PROJ_INFO)); - - /* Expected accuracy of the transformation. Hardcoded for now, will be improved */ - /* later. Most likely to be used when a transformation is set up with */ - /* proj_create_crs_to_crs in a future version that leverages the EPSG database. */ - pjinfo.accuracy = -1.0; - - if (0==P) - return pjinfo; - - /* projection id */ - if (pj_param(P->ctx, P->params, "tproj").i) - pjinfo.id = pj_param(P->ctx, P->params, "sproj").s; - - /* projection description */ - pjinfo.description = P->descr; - - /* projection definition */ - if (P->def_full) - def = P->def_full; - else - def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ - if (0==def) - pjinfo.definition = empty; - else - pjinfo.definition = pj_shrink (def); - /* Make pj_free clean this up eventually */ - P->def_full = def; - - pjinfo.has_inverse = pj_has_inverse(P); - return pjinfo; -} - - -/*****************************************************************************/ -PJ_GRID_INFO proj_grid_info(const char *gridname) { -/****************************************************************************** - Information about a named datum grid. - - Returns PJ_GRID_INFO struct. -******************************************************************************/ - PJ_GRID_INFO grinfo; - - /*PJ_CONTEXT *ctx = proj_context_create(); */ - PJ_CONTEXT *ctx = pj_get_default_ctx(); - PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); - memset(&grinfo, 0, sizeof(PJ_GRID_INFO)); - - /* in case the grid wasn't found */ - if (gridinfo->filename == NULL) { - pj_gridinfo_free(ctx, gridinfo); - strcpy(grinfo.format, "missing"); - return grinfo; - } - - /* The string copies below are automatically null-terminated due to */ - /* the memset above, so strncpy is safe */ - - /* name of grid */ - strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); - - /* full path of grid */ - pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1); - - /* grid format */ - strncpy (grinfo.format, gridinfo->format, sizeof(grinfo.format) - 1); - - /* grid size */ - grinfo.n_lon = gridinfo->ct->lim.lam; - grinfo.n_lat = gridinfo->ct->lim.phi; - - /* cell size */ - grinfo.cs_lon = gridinfo->ct->del.lam; - grinfo.cs_lat = gridinfo->ct->del.phi; - - /* bounds of grid */ - grinfo.lowerleft = gridinfo->ct->ll; - grinfo.upperright.lam = grinfo.lowerleft.lam + grinfo.n_lon*grinfo.cs_lon; - grinfo.upperright.phi = grinfo.lowerleft.phi + grinfo.n_lat*grinfo.cs_lat; - - pj_gridinfo_free(ctx, gridinfo); - - return grinfo; -} - - - -/*****************************************************************************/ -PJ_INIT_INFO proj_init_info(const char *initname){ -/****************************************************************************** - Information about a named init file. - - Maximum length of initname is 64. - - Returns PJ_INIT_INFO struct. - - If the init file is not found all members of the return struct are set - to the empty string. - - If the init file is found, but the metadata is missing, the value is - set to "Unknown". -******************************************************************************/ - int file_found; - char param[80], key[74]; - paralist *start, *next; - PJ_INIT_INFO ininfo; - PJ_CONTEXT *ctx = pj_get_default_ctx(); - - memset(&ininfo, 0, sizeof(PJ_INIT_INFO)); - - file_found = pj_find_file(ctx, initname, ininfo.filename, sizeof(ininfo.filename)); - if (!file_found || strlen(initname) > 64) { - if( strcmp(initname, "epsg") == 0 || strcmp(initname, "EPSG") == 0 ) { - const char* val; - - pj_ctx_set_errno( ctx, 0 ); - - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); - strcpy(ininfo.origin, "EPSG"); - val = proj_context_get_database_metadata(ctx, "EPSG.VERSION"); - if( val ) { - strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); - } - val = proj_context_get_database_metadata(ctx, "EPSG.DATE"); - if( val ) { - strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); - } - return ininfo; - } - - if( strcmp(initname, "IGNF") == 0 ) { - const char* val; - - pj_ctx_set_errno( ctx, 0 ); - - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); - strcpy(ininfo.origin, "IGNF"); - val = proj_context_get_database_metadata(ctx, "IGNF.VERSION"); - if( val ) { - strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); - } - val = proj_context_get_database_metadata(ctx, "IGNF.DATE"); - if( val ) { - strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); - } - return ininfo; - } - - return ininfo; - } - - /* The initial memset (0) makes strncpy safe here */ - strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); - strcpy(ininfo.origin, "Unknown"); - strcpy(ininfo.version, "Unknown"); - strcpy(ininfo.lastupdate, "Unknown"); - - strncpy (key, initname, 64); /* make room for ":metadata\0" at the end */ - key[64] = 0; - memcpy(key + strlen(key), ":metadata", 9 + 1); - strcpy(param, "+init="); - /* The +strlen(param) avoids a cppcheck false positive warning */ - strncat(param + strlen(param), key, sizeof(param)-1-strlen(param)); - - start = pj_mkparam(param); - pj_expand_init(ctx, start); - - if (pj_param(ctx, start, "tversion").i) - strncpy(ininfo.version, pj_param(ctx, start, "sversion").s, sizeof(ininfo.version) - 1); - - if (pj_param(ctx, start, "torigin").i) - strncpy(ininfo.origin, pj_param(ctx, start, "sorigin").s, sizeof(ininfo.origin) - 1); - - if (pj_param(ctx, start, "tlastupdate").i) - strncpy(ininfo.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(ininfo.lastupdate) - 1); - - for ( ; start; start = next) { - next = start->next; - pj_dalloc(start); - } - - return ininfo; -} - - - -/*****************************************************************************/ -PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { -/****************************************************************************** - Cartographic characteristics at point lp. - - Characteristics include meridian, parallel and areal scales, angular - distortion, meridian/parallel, meridian convergence and scale error. - - returns PJ_FACTORS. If unsuccessful, error number is set and the - struct returned contains NULL data. -******************************************************************************/ - PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0, 0,0,0,0}; - struct FACTORS f; - - if (0==P) - return factors; - - if (pj_factors(lp.lp, P, 0.0, &f)) - return factors; - - factors.meridional_scale = f.h; - factors.parallel_scale = f.k; - factors.areal_scale = f.s; - - factors.angular_distortion = f.omega; - factors.meridian_parallel_angle = f.thetap; - factors.meridian_convergence = f.conv; - - factors.tissot_semimajor = f.a; - factors.tissot_semiminor = f.b; - - /* Raw derivatives, for completeness's sake */ - factors.dx_dlam = f.der.x_l; - factors.dx_dphi = f.der.x_p; - factors.dy_dlam = f.der.y_l; - factors.dy_dphi = f.der.y_p; - - return factors; -} diff --git a/src/proj_4D_api.cpp b/src/proj_4D_api.cpp new file mode 100644 index 00000000..c2a37a49 --- /dev/null +++ b/src/proj_4D_api.cpp @@ -0,0 +1,1285 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Implement a (currently minimalistic) proj API based primarily + * on the PJ_COORD 4D geodetic spatiotemporal data type. + * + * Author: Thomas Knudsen, thokn@sdfe.dk, 2016-06-09/2016-11-06 + * + ****************************************************************************** + * Copyright (c) 2016, 2017 Thomas Knudsen/SDFE + * + * 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 +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +#include "proj.h" +#include "proj_internal.h" +#include "proj_math.h" +#include "projects.h" +#include "geodesic.h" + + +/* Initialize PJ_COORD struct */ +PJ_COORD proj_coord (double x, double y, double z, double t) { + PJ_COORD res; + res.v[0] = x; + res.v[1] = y; + res.v[2] = z; + res.v[3] = t; + return res; +} + +static PJ_DIRECTION opposite_direction(PJ_DIRECTION dir) { + return static_cast(-dir); +} + +/*****************************************************************************/ +int proj_angular_input (PJ *P, enum PJ_DIRECTION dir) { +/****************************************************************************** + Returns 1 if the operator P expects angular input coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} +******************************************************************************/ + if (PJ_FWD==dir) + return pj_left (P)==PJ_IO_UNITS_ANGULAR; + return pj_right (P)==PJ_IO_UNITS_ANGULAR; +} + +/*****************************************************************************/ +int proj_angular_output (PJ *P, enum PJ_DIRECTION dir) { +/****************************************************************************** + Returns 1 if the operator P provides angular output coordinates when + operating in direction dir, 0 otherwise. + dir: {PJ_FWD, PJ_INV} +******************************************************************************/ + return proj_angular_input (P, opposite_direction(dir)); +} + + +/* Geodesic distance (in meter) + fwd and rev azimuth between two points on the ellipsoid */ +PJ_COORD proj_geod (const PJ *P, PJ_COORD a, PJ_COORD b) { + PJ_COORD c; + /* Note: the geodesic code takes arguments in degrees */ + geod_inverse (P->geod, + PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), + c.v, c.v+1, c.v+2 + ); + + return c; +} + + +/* Geodesic distance (in meter) between two points with angular 2D coordinates */ +double proj_lp_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { + double s12, azi1, azi2; + /* Note: the geodesic code takes arguments in degrees */ + geod_inverse (P->geod, + PJ_TODEG(a.lpz.phi), PJ_TODEG(a.lpz.lam), + PJ_TODEG(b.lpz.phi), PJ_TODEG(b.lpz.lam), + &s12, &azi1, &azi2 + ); + return s12; +} + +/* The geodesic distance AND the vertical offset */ +double proj_lpz_dist (const PJ *P, PJ_COORD a, PJ_COORD b) { + if (HUGE_VAL==a.lpz.lam || HUGE_VAL==b.lpz.lam) + return HUGE_VAL; + return hypot (proj_lp_dist (P, a, b), a.lpz.z - b.lpz.z); +} + +/* Euclidean distance between two points with linear 2D coordinates */ +double proj_xy_dist (PJ_COORD a, PJ_COORD b) { + return hypot (a.xy.x - b.xy.x, a.xy.y - b.xy.y); +} + +/* Euclidean distance between two points with linear 3D coordinates */ +double proj_xyz_dist (PJ_COORD a, PJ_COORD b) { + return hypot (proj_xy_dist (a, b), a.xyz.z - b.xyz.z); +} + + + +/* Measure numerical deviation after n roundtrips fwd-inv (or inv-fwd) */ +double proj_roundtrip (PJ *P, PJ_DIRECTION direction, int n, PJ_COORD *coord) { + int i; + PJ_COORD t, org; + + if (0==P) + return HUGE_VAL; + + if (n < 1) { + proj_errno_set (P, EINVAL); + return HUGE_VAL; + } + + /* in the first half-step, we generate the output value */ + org = *coord; + *coord = proj_trans (P, direction, org); + t = *coord; + + /* now we take n-1 full steps in inverse direction: We are */ + /* out of phase due to the half step already taken */ + for (i = 0; i < n - 1; i++) + t = proj_trans (P, direction, proj_trans (P, opposite_direction(direction), t) ); + + /* finally, we take the last half-step */ + t = proj_trans (P, opposite_direction(direction), t); + + /* checking for angular *input* since we do a roundtrip, and end where we begin */ + if (proj_angular_input (P, direction)) + return proj_lpz_dist (P, org, t); + + return proj_xyz_dist (org, t); +} + + + +/**************************************************************************************/ +PJ_COORD proj_trans (PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { +/*************************************************************************************** +Apply the transformation P to the coordinate coord, preferring the 4D interfaces if +available. + +See also pj_approx_2D_trans and pj_approx_3D_trans in pj_internal.c, which work +similarly, but prefers the 2D resp. 3D interfaces if available. +***************************************************************************************/ + if (0==P) + return coord; + if (P->inverted) + direction = opposite_direction(direction); + + switch (direction) { + case PJ_FWD: + return pj_fwd4d (coord, P); + case PJ_INV: + return pj_inv4d (coord, P); + case PJ_IDENT: + return coord; + default: + break; + } + + proj_errno_set (P, EINVAL); + return proj_coord_error (); +} + + + +/*****************************************************************************/ +int proj_trans_array (PJ *P, PJ_DIRECTION direction, size_t n, PJ_COORD *coord) { +/****************************************************************************** + Batch transform an array of PJ_COORD. + + Returns 0 if all coordinates are transformed without error, otherwise + returns error number. +******************************************************************************/ + size_t i; + + for (i = 0; i < n; i++) { + coord[i] = proj_trans (P, direction, coord[i]); + if (proj_errno(P)) + return proj_errno (P); + } + + return 0; +} + + + +/*************************************************************************************/ +size_t proj_trans_generic ( + PJ *P, + PJ_DIRECTION direction, + double *x, size_t sx, size_t nx, + double *y, size_t sy, size_t ny, + double *z, size_t sz, size_t nz, + double *t, size_t st, size_t nt +) { +/************************************************************************************** + + Transform a series of coordinates, where the individual coordinate dimension + may be represented by an array that is either + + 1. fully populated + 2. a null pointer and/or a length of zero, which will be treated as a + fully populated array of zeroes + 3. of length one, i.e. a constant, which will be treated as a fully + populated array of that constant value + + The strides, sx, sy, sz, st, represent the step length, in bytes, between + consecutive elements of the corresponding array. This makes it possible for + proj_transform to handle transformation of a large class of application + specific data structures, without necessarily understanding the data structure + format, as in: + + typedef struct {double x, y; int quality_level; char surveyor_name[134];} XYQS; + XYQS survey[345]; + double height = 23.45; + PJ *P = {...}; + size_t stride = sizeof (XYQS); + ... + proj_transform ( + P, PJ_INV, sizeof(XYQS), + &(survey[0].x), stride, 345, (* We have 345 eastings *) + &(survey[0].y), stride, 345, (* ...and 345 northings. *) + &height, 1, (* The height is the constant 23.45 m *) + 0, 0 (* and the time is the constant 0.00 s *) + ); + + This is similar to the inner workings of the pj_transform function, but the + stride functionality has been generalized to work for any size of basic unit, + not just a fixed number of doubles. + + In most cases, the stride will be identical for x, y,z, and t, since they will + typically be either individual arrays (stride = sizeof(double)), or strided + views into an array of application specific data structures (stride = sizeof (...)). + + But in order to support cases where x, y, z, and t come from heterogeneous + sources, individual strides, sx, sy, sz, st, are used. + + Caveat: Since proj_transform does its work *in place*, this means that even the + supposedly constants (i.e. length 1 arrays) will return from the call in altered + state. Hence, remember to reinitialize between repeated calls. + + Return value: Number of transformations completed. + +**************************************************************************************/ + PJ_COORD coord = {{0,0,0,0}}; + size_t i, nmin; + double null_broadcast = 0; + + if (0==P) + return 0; + + if (P->inverted) + direction = opposite_direction(direction); + + /* ignore lengths of null arrays */ + if (0==x) nx = 0; + if (0==y) ny = 0; + if (0==z) nz = 0; + if (0==t) nt = 0; + + /* and make the nullities point to some real world memory for broadcasting nulls */ + if (0==nx) x = &null_broadcast; + if (0==ny) y = &null_broadcast; + if (0==nz) z = &null_broadcast; + if (0==nt) t = &null_broadcast; + + /* nothing to do? */ + if (0==nx+ny+nz+nt) + return 0; + + /* arrays of length 1 are constants, which we broadcast along the longer arrays */ + /* so we need to find the length of the shortest non-unity array to figure out */ + /* how many coordinate pairs we must transform */ + nmin = (nx > 1)? nx: (ny > 1)? ny: (nz > 1)? nz: (nt > 1)? nt: 1; + if ((nx > 1) && (nx < nmin)) nmin = nx; + if ((ny > 1) && (ny < nmin)) nmin = ny; + if ((nz > 1) && (nz < nmin)) nmin = nz; + if ((nt > 1) && (nt < nmin)) nmin = nt; + + /* Check validity of direction flag */ + switch (direction) { + case PJ_FWD: + case PJ_INV: + break; + case PJ_IDENT: + return nmin; + default: + proj_errno_set (P, EINVAL); + return 0; + } + + /* Arrays of length==0 are broadcast as the constant 0 */ + /* Arrays of length==1 are broadcast as their single value */ + /* Arrays of length >1 are iterated over (for the first nmin values) */ + /* The slightly convolved incremental indexing is used due */ + /* to the stride, which may be any size supported by the platform */ + for (i = 0; i < nmin; i++) { + coord.xyzt.x = *x; + coord.xyzt.y = *y; + coord.xyzt.z = *z; + coord.xyzt.t = *t; + + if (PJ_FWD==direction) + coord = pj_fwd4d (coord, P); + else + coord = pj_inv4d (coord, P); + + /* in all full length cases, we overwrite the input with the output, */ + /* and step on to the next element. */ + /* The casts are somewhat funky, but they compile down to no-ops and */ + /* they tell compilers and static analyzers that we know what we do */ + if (nx > 1) { + *x = coord.xyzt.x; + x = (double *) ((void *) ( ((char *) x) + sx)); + } + if (ny > 1) { + *y = coord.xyzt.y; + y = (double *) ((void *) ( ((char *) y) + sy)); + } + if (nz > 1) { + *z = coord.xyzt.z; + z = (double *) ((void *) ( ((char *) z) + sz)); + } + if (nt > 1) { + *t = coord.xyzt.t; + t = (double *) ((void *) ( ((char *) t) + st)); + } + } + + /* Last time around, we update the length 1 cases with their transformed alter egos */ + if (nx==1) + *x = coord.xyzt.x; + if (ny==1) + *y = coord.xyzt.y; + if (nz==1) + *z = coord.xyzt.z; + if (nt==1) + *t = coord.xyzt.t; + + return i; +} + + +/*************************************************************************************/ +PJ_COORD pj_geocentric_latitude (const PJ *P, PJ_DIRECTION direction, PJ_COORD coord) { +/************************************************************************************** + Convert geographical latitude to geocentric (or the other way round if + direction = PJ_INV) + + The conversion involves a call to the tangent function, which goes through the + roof at the poles, so very close (the last centimeter) to the poles no + conversion takes place and the input latitude is copied directly to the output. + + Fortunately, the geocentric latitude converges to the geographical at the + poles, so the difference is negligible. + + For the spherical case, the geographical latitude equals the geocentric, and + consequently, the input is copied directly to the output. +**************************************************************************************/ + const double limit = M_HALFPI - 1e-9; + PJ_COORD res = coord; + if ((coord.lp.phi > limit) || (coord.lp.phi < -limit) || (P->es==0)) + return res; + if (direction==PJ_FWD) + res.lp.phi = atan (P->one_es * tan (coord.lp.phi) ); + else + res.lp.phi = atan (P->rone_es * tan (coord.lp.phi) ); + + return res; +} + +double proj_torad (double angle_in_degrees) { return PJ_TORAD (angle_in_degrees);} +double proj_todeg (double angle_in_radians) { return PJ_TODEG (angle_in_radians);} + +double proj_dmstor(const char *is, char **rs) { + return dmstor(is, rs); +} + +char* proj_rtodms(char *s, double r, int pos, int neg) { + return rtodms(s, r, pos, neg); +} + +/*************************************************************************************/ +static PJ* skip_prep_fin(PJ *P) { +/************************************************************************************** +Skip prepare and finalize function for the various "helper operations" added to P when +in cs2cs compatibility mode. +**************************************************************************************/ + P->skip_fwd_prepare = 1; + P->skip_fwd_finalize = 1; + P->skip_inv_prepare = 1; + P->skip_inv_finalize = 1; + return P; +} + +/*************************************************************************************/ +static int cs2cs_emulation_setup (PJ *P) { +/************************************************************************************** +If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the 4D API +equivalent operations, so the preparation and finalization steps in the pj_inv/pj_fwd +invocators can emulate the behaviour of pj_transform and the cs2cs app. + +Returns 1 on success, 0 on failure +**************************************************************************************/ + PJ *Q; + paralist *p; + int do_cart = 0; + if (0==P) + return 0; + + /* Don't recurse when calling proj_create (which calls us back) */ + if (pj_param_exists (P->params, "break_cs2cs_recursion")) + return 1; + + /* Swap axes? */ + p = pj_param_exists (P->params, "axis"); + + /* Don't axisswap if data are already in "enu" order */ + if (p && (0!=strcmp ("enu", p->param))) { + char *def = static_cast(malloc (100+strlen(P->axis))); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=axisswap axis=%s", P->axis); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->axisswap = skip_prep_fin(Q); + } + + /* Geoid grid(s) given? */ + p = pj_param_exists (P->params, "geoidgrids"); + if (p && strlen (p->param) > strlen ("geoidgrids=")) { + char *gridnames = p->param + strlen ("geoidgrids="); + char *def = static_cast(malloc (100+strlen(gridnames))); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=vgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->vgridshift = skip_prep_fin(Q); + } + + /* Datum shift grid(s) given? */ + p = pj_param_exists (P->params, "nadgrids"); + if (p && strlen (p->param) > strlen ("nadgrids=")) { + char *gridnames = p->param + strlen ("nadgrids="); + char *def = static_cast(malloc (100+strlen(gridnames))); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=hgridshift grids=%s", gridnames); + Q = proj_create (P->ctx, def); + free (def); + if (0==Q) + return 0; + P->hgridshift = skip_prep_fin(Q); + } + + /* We ignore helmert if we have grid shift */ + p = P->hgridshift ? 0 : pj_param_exists (P->params, "towgs84"); + while (p) { + char *def; + char *s = p->param; + double *d = P->datum_params; + size_t n = strlen (s); + + /* We ignore null helmert shifts (common in auto-translated resource files, e.g. epsg) */ + if (0==d[0] && 0==d[1] && 0==d[2] && 0==d[3] && 0==d[4] && 0==d[5] && 0==d[6]) { + /* If the current ellipsoid is not WGS84, then make sure the */ + /* change in ellipsoid is still done. */ + if (!(fabs(P->a_orig - 6378137.0) < 1e-8 && fabs(P->es_orig - 0.0066943799901413) < 1e-15)) { + do_cart = 1; + } + break; + } + + if (n <= 8) /* 8==strlen ("towgs84=") */ + return 0; + + def = static_cast(malloc (100+n)); + if (0==def) + return 0; + sprintf (def, "break_cs2cs_recursion proj=helmert exact %s convention=position_vector", s); + Q = proj_create (P->ctx, def); + free(def); + if (0==Q) + return 0; + pj_inherit_ellipsoid_def (P, Q); + P->helmert = skip_prep_fin (Q); + + break; + } + + /* We also need cartesian/geographical transformations if we are working in */ + /* geocentric/cartesian space or we need to do a Helmert transform. */ + if (P->is_geocent || P->helmert || do_cart) { + char def[150]; + sprintf (def, "break_cs2cs_recursion proj=cart a=%40.20g es=%40.20g", P->a_orig, P->es_orig); + { + /* In case the current locale does not use dot but comma as decimal */ + /* separator, replace it with dot, so that proj_atof() behaves */ + /* correctly. */ + /* TODO later: use C++ ostringstream with imbue(std::locale::classic()) */ + /* to be locale unaware */ + char* next_pos; + for (next_pos = def; (next_pos = strchr (next_pos, ',')) != NULL; next_pos++) { + *next_pos = '.'; + } + } + Q = proj_create (P->ctx, def); + if (0==Q) + return 0; + P->cart = skip_prep_fin (Q); + + if (!P->is_geocent) { + sprintf (def, "break_cs2cs_recursion proj=cart ellps=WGS84"); + Q = proj_create (P->ctx, def); + if (0==Q) + return 0; + P->cart_wgs84 = skip_prep_fin (Q); + } + } + + return 1; +} + + + +/*************************************************************************************/ +PJ *proj_create (PJ_CONTEXT *ctx, const char *definition) { +/************************************************************************************** + Create a new PJ object in the context ctx, using the given definition. If ctx==0, + the default context is used, if definition==0, or invalid, a null-pointer is + returned. The definition may use '+' as argument start indicator, as in + "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32". + + It may even use free formatting "proj = utm; zone =32 ellps= GRS80". + Note that the semicolon separator is allowed, but not required. +**************************************************************************************/ + PJ *P; + char *args, **argv; + size_t argc, n; + int ret; + int allow_init_epsg; + + if (0==ctx) + ctx = pj_get_default_ctx (); + + /* Make a copy that we can manipulate */ + n = strlen (definition); + args = (char *) malloc (n + 1); + if (0==args) { + proj_context_errno_set(ctx, ENOMEM); + return 0; + } + strcpy (args, definition); + + argc = pj_trim_argc (args); + if (argc==0) { + pj_dealloc (args); + proj_context_errno_set(ctx, PJD_ERR_NO_ARGS); + return 0; + } + + argv = pj_trim_argv (argc, args); + + /* ...and let pj_init_ctx do the hard work */ + /* New interface: forbid init=epsg:XXXX syntax by default */ + allow_init_epsg = proj_context_get_use_proj4_init_rules(ctx, FALSE); + P = pj_init_ctx_with_allow_init_epsg (ctx, (int) argc, argv, allow_init_epsg); + + pj_dealloc (argv); + pj_dealloc (args); + + /* Support cs2cs-style modifiers */ + ret = cs2cs_emulation_setup (P); + if (0==ret) + return proj_destroy (P); + + return P; +} + + + +/*************************************************************************************/ +PJ *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv) { +/************************************************************************************** +Create a new PJ object in the context ctx, using the given definition argument +array argv. If ctx==0, the default context is used, if definition==0, or invalid, +a null-pointer is returned. The definition arguments may use '+' as argument start +indicator, as in {"+proj=utm", "+zone=32"}, or leave it out, as in {"proj=utm", +"zone=32"}. +**************************************************************************************/ + PJ *P; + const char *c; + + if (0==ctx) + ctx = pj_get_default_ctx (); + if (0==argv) { + proj_context_errno_set(ctx, PJD_ERR_NO_ARGS); + return 0; + } + + /* We assume that free format is used, and build a full proj_create compatible string */ + c = pj_make_args (argc, argv); + if (0==c) { + proj_context_errno_set(ctx, ENOMEM); + return 0; + } + + P = proj_create (ctx, c); + + pj_dealloc ((char *) c); + return P; +} + +/** Create an area of use */ +PJ_AREA * proj_area_create(void) { + return static_cast(pj_calloc(1, sizeof(PJ_AREA))); +} + +/** Assign a bounding box to an area of use. */ +void proj_area_set_bbox(PJ_AREA *area, + double west_lon_degree, + double south_lat_degree, + double east_lon_degree, + double north_lat_degree) { + area->bbox_set = TRUE; + area->west_lon_degree = west_lon_degree; + area->south_lat_degree = south_lat_degree; + area->east_lon_degree = east_lon_degree; + area->north_lat_degree = north_lat_degree; +} + +/** Free an area of use */ +void proj_area_destroy(PJ_AREA* area) { + pj_dealloc(area); +} + +/************************************************************************/ +/* proj_context_use_proj4_init_rules() */ +/************************************************************************/ + +void proj_context_use_proj4_init_rules(PJ_CONTEXT *ctx, int enable) { + if( ctx == NULL ) { + ctx = pj_get_default_ctx(); + } + ctx->use_proj4_init_rules = enable; +} + +/************************************************************************/ +/* EQUAL() */ +/************************************************************************/ + +static int EQUAL(const char* a, const char* b) { +#ifdef _MSC_VER + return _stricmp(a, b) == 0; +#else + return strcasecmp(a, b) == 0; +#endif +} + +/************************************************************************/ +/* proj_context_get_use_proj4_init_rules() */ +/************************************************************************/ + +int proj_context_get_use_proj4_init_rules(PJ_CONTEXT *ctx, int from_legacy_code_path) { + const char* val = getenv("PROJ_USE_PROJ4_INIT_RULES"); + + if( ctx == NULL ) { + ctx = pj_get_default_ctx(); + } + + if( val ) { + if( EQUAL(val, "yes") || EQUAL(val, "on") || EQUAL(val, "true") ) { + return TRUE; + } + if( EQUAL(val, "no") || EQUAL(val, "off") || EQUAL(val, "false") ) { + return FALSE; + } + pj_log(ctx, PJ_LOG_ERROR, "Invalid value for PROJ_USE_PROJ4_INIT_RULES"); + } + + if( ctx->use_proj4_init_rules >= 0 ) { + return ctx->use_proj4_init_rules; + } + return from_legacy_code_path; +} + + +/*****************************************************************************/ +PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char *target_crs, PJ_AREA *area) { +/****************************************************************************** + Create a transformation pipeline between two known coordinate reference + systems. + + source_crs and target_crs can be : + - a "AUTHORITY:CODE", like EPSG:25832. When using that syntax for a source + CRS, the created pipeline will expect that the values passed to proj_trans() + respect the axis order and axis unit of the official definition ( + so for example, for EPSG:4326, with latitude first and longitude next, + in degrees). Similarly, when using that syntax for a target CRS, output + values will be emitted according to the official definition of this CRS. + - a PROJ string, like "+proj=longlat +datum=WGS84". + When using that syntax, the axis order and unit for geographic CRS will + be longitude, latitude, and the unit degrees. + - more generally any string accepted by proj_obj_create_from_user_input() + + An "area of use" can be specified in area. When it is supplied, the more + accurate transformation between two given systems can be chosen. + + Example call: + + PJ *P = proj_create_crs_to_crs(0, "EPSG:25832", "EPSG:25833", NULL); + +******************************************************************************/ + PJ *P; + PJ_OBJ* src; + PJ_OBJ* dst; + PJ_OPERATION_FACTORY_CONTEXT* operation_ctx; + PJ_OBJ_LIST* op_list; + PJ_OBJ* op; + const char* proj_string; + const char* const optionsProj4Mode[] = { "USE_PROJ4_INIT_RULES=YES", NULL }; + const char* const* optionsImportCRS = + proj_context_get_use_proj4_init_rules(ctx, FALSE) ? optionsProj4Mode : NULL; + + src = proj_obj_create_from_user_input(ctx, source_crs, optionsImportCRS); + if( !src ) { + return NULL; + } + + dst = proj_obj_create_from_user_input(ctx, target_crs, optionsImportCRS); + if( !dst ) { + proj_obj_destroy(src); + return NULL; + } + + operation_ctx = proj_create_operation_factory_context(ctx, NULL); + if( !operation_ctx ) { + proj_obj_destroy(src); + proj_obj_destroy(dst); + return NULL; + } + + if( area && area->bbox_set ) { + proj_operation_factory_context_set_area_of_interest( + ctx, + operation_ctx, + area->west_lon_degree, + area->south_lat_degree, + area->east_lon_degree, + area->north_lat_degree); + } + + proj_operation_factory_context_set_grid_availability_use( + ctx, operation_ctx, PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID); + + op_list = proj_obj_create_operations(ctx, src, dst, operation_ctx); + + proj_operation_factory_context_destroy(operation_ctx); + proj_obj_destroy(src); + proj_obj_destroy(dst); + + if( !op_list ) { + return NULL; + } + + if( proj_obj_list_get_count(op_list) == 0 ) { + proj_obj_list_destroy(op_list); + return NULL; + } + + op = proj_obj_list_get(ctx, op_list, 0); + proj_obj_list_destroy(op_list); + if( !op ) { + return NULL; + } + + proj_string = proj_obj_as_proj_string(ctx, op, PJ_PROJ_5, NULL); + if( !proj_string) { + proj_obj_destroy(op); + return NULL; + } + + if( proj_string[0] == '\0' ) { + /* Null transform ? */ + P = proj_create(ctx, "proj=affine"); + } else { + P = proj_create(ctx, proj_string); + } + + proj_obj_destroy(op); + + return P; +} + +PJ *proj_destroy (PJ *P) { + pj_free (P); + return 0; +} + +/*****************************************************************************/ +int proj_errno (const PJ *P) { +/****************************************************************************** + Read an error level from the context of a PJ. +******************************************************************************/ + return pj_ctx_get_errno (pj_get_ctx ((PJ *) P)); +} + +/*****************************************************************************/ +int proj_context_errno (PJ_CONTEXT *ctx) { +/****************************************************************************** + Read an error directly from a context, without going through a PJ + belonging to that context. +******************************************************************************/ + if (0==ctx) + ctx = pj_get_default_ctx(); + return pj_ctx_get_errno (ctx); +} + +/*****************************************************************************/ +int proj_errno_set (const PJ *P, int err) { +/****************************************************************************** + Set context-errno, bubble it up to the thread local errno, return err +******************************************************************************/ + /* Use proj_errno_reset to explicitly clear the error status */ + if (0==err) + return 0; + + /* For P==0 err goes to the default context */ + proj_context_errno_set (pj_get_ctx ((PJ *) P), err); + errno = err; + return err; +} + +/*****************************************************************************/ +int proj_errno_restore (const PJ *P, int err) { +/****************************************************************************** + Use proj_errno_restore when the current function succeeds, but the + error flag was set on entry, and stored/reset using proj_errno_reset + in order to monitor for new errors. + + See usage example under proj_errno_reset () +******************************************************************************/ + if (0==err) + return 0; + proj_errno_set (P, err); + return 0; +} + +/*****************************************************************************/ +int proj_errno_reset (const PJ *P) { +/****************************************************************************** + Clears errno in the context and thread local levels + through the low level pj_ctx interface. + + Returns the previous value of the errno, for convenient reset/restore + operations: + + int foo (PJ *P) { + // errno may be set on entry, but we need to reset it to be able to + // check for errors from "do_something_with_P(P)" + int last_errno = proj_errno_reset (P); + + // local failure + if (0==P) + return proj_errno_set (P, 42); + + // call to function that may fail + do_something_with_P (P); + + // failure in do_something_with_P? - keep latest error status + if (proj_errno(P)) + return proj_errno (P); + + // success - restore previous error status, return 0 + return proj_errno_restore (P, last_errno); + } +******************************************************************************/ + int last_errno; + last_errno = proj_errno (P); + + pj_ctx_set_errno (pj_get_ctx ((PJ *) P), 0); + errno = 0; + pj_errno = 0; + return last_errno; +} + + +/* Create a new context */ +PJ_CONTEXT *proj_context_create (void) { + return pj_ctx_alloc (); +} + + +PJ_CONTEXT *proj_context_destroy (PJ_CONTEXT *ctx) { + if (0==ctx) + return 0; + + /* Trying to free the default context is a no-op (since it is statically allocated) */ + if (pj_get_default_ctx ()==ctx) + return 0; + + pj_ctx_free (ctx); + return 0; +} + + + + + + +/*****************************************************************************/ +static char *path_append (char *buf, const char *app, size_t *buf_size) { +/****************************************************************************** + Helper for proj_info() below. Append app to buf, separated by a + semicolon. Also handle allocation of longer buffer if needed. + + Returns buffer and adjusts *buf_size through provided pointer arg. +******************************************************************************/ + char *p; + size_t len, applen = 0, buflen = 0; +#ifdef _WIN32 + char *delim = ";"; +#else + char *delim = ":"; +#endif + + /* Nothing to do? */ + if (0 == app) + return buf; + applen = strlen (app); + if (0 == applen) + return buf; + + /* Start checking whether buf is long enough */ + if (0 != buf) + buflen = strlen (buf); + len = buflen+applen+strlen (delim) + 1; + + /* "pj_realloc", so to speak */ + if (*buf_size < len) { + p = static_cast(pj_calloc (2 * len, sizeof (char))); + if (0==p) { + pj_dealloc (buf); + return 0; + } + *buf_size = 2 * len; + if (buf != 0) + strcpy (p, buf); + pj_dealloc (buf); + buf = p; + } + + /* Only append a semicolon if something's already there */ + if (0 != buflen) + strcat (buf, ";"); + strcat (buf, app); + return buf; +} + +static const char *empty = {""}; +static char version[64] = {""}; +static PJ_INFO info = {0, 0, 0, 0, 0, 0, 0, 0}; +static volatile int info_initialized = 0; + +/*****************************************************************************/ +PJ_INFO proj_info (void) { +/****************************************************************************** + Basic info about the current instance of the PROJ.4 library. + + Returns PJ_INFO struct. +******************************************************************************/ + const char * const *paths; + size_t i, n; + + size_t buf_size = 0; + char *buf = 0; + + pj_acquire_lock (); + + if (0!=info_initialized) { + pj_release_lock (); + return info; + } + + info.major = PROJ_VERSION_MAJOR; + info.minor = PROJ_VERSION_MINOR; + info.patch = PROJ_VERSION_PATCH; + + /* This is a controlled environment, so no risk of sprintf buffer + overflow. A normal version string is xx.yy.zz which is 8 characters + long and there is room for 64 bytes in the version string. */ + sprintf (version, "%d.%d.%d", info.major, info.minor, info.patch); + + info.searchpath = empty; + info.version = version; + info.release = pj_get_release (); + + /* build search path string */ + buf = path_append (buf, getenv ("HOME"), &buf_size); + buf = path_append (buf, getenv ("PROJ_LIB"), &buf_size); + + paths = proj_get_searchpath (); + n = (size_t) proj_get_path_count (); + + for (i = 0; i < n; i++) + buf = path_append (buf, paths[i], &buf_size); + info.searchpath = buf ? buf : empty; + + info.paths = paths; + info.path_count = n; + + info_initialized = 1; + pj_release_lock (); + return info; +} + + +/*****************************************************************************/ +PJ_PROJ_INFO proj_pj_info(PJ *P) { +/****************************************************************************** + Basic info about a particular instance of a projection object. + + Returns PJ_PROJ_INFO struct. +******************************************************************************/ + PJ_PROJ_INFO pjinfo; + char *def; + + memset(&pjinfo, 0, sizeof(PJ_PROJ_INFO)); + + /* Expected accuracy of the transformation. Hardcoded for now, will be improved */ + /* later. Most likely to be used when a transformation is set up with */ + /* proj_create_crs_to_crs in a future version that leverages the EPSG database. */ + pjinfo.accuracy = -1.0; + + if (0==P) + return pjinfo; + + /* projection id */ + if (pj_param(P->ctx, P->params, "tproj").i) + pjinfo.id = pj_param(P->ctx, P->params, "sproj").s; + + /* projection description */ + pjinfo.description = P->descr; + + /* projection definition */ + if (P->def_full) + def = P->def_full; + else + def = pj_get_def(P, 0); /* pj_get_def takes a non-const PJ pointer */ + if (0==def) + pjinfo.definition = empty; + else + pjinfo.definition = pj_shrink (def); + /* Make pj_free clean this up eventually */ + P->def_full = def; + + pjinfo.has_inverse = pj_has_inverse(P); + return pjinfo; +} + + +/*****************************************************************************/ +PJ_GRID_INFO proj_grid_info(const char *gridname) { +/****************************************************************************** + Information about a named datum grid. + + Returns PJ_GRID_INFO struct. +******************************************************************************/ + PJ_GRID_INFO grinfo; + + /*PJ_CONTEXT *ctx = proj_context_create(); */ + PJ_CONTEXT *ctx = pj_get_default_ctx(); + PJ_GRIDINFO *gridinfo = pj_gridinfo_init(ctx, gridname); + memset(&grinfo, 0, sizeof(PJ_GRID_INFO)); + + /* in case the grid wasn't found */ + if (gridinfo->filename == NULL) { + pj_gridinfo_free(ctx, gridinfo); + strcpy(grinfo.format, "missing"); + return grinfo; + } + + /* The string copies below are automatically null-terminated due to */ + /* the memset above, so strncpy is safe */ + + /* name of grid */ + strncpy (grinfo.gridname, gridname, sizeof(grinfo.gridname) - 1); + + /* full path of grid */ + pj_find_file(ctx, gridname, grinfo.filename, sizeof(grinfo.filename) - 1); + + /* grid format */ + strncpy (grinfo.format, gridinfo->format, sizeof(grinfo.format) - 1); + + /* grid size */ + grinfo.n_lon = gridinfo->ct->lim.lam; + grinfo.n_lat = gridinfo->ct->lim.phi; + + /* cell size */ + grinfo.cs_lon = gridinfo->ct->del.lam; + grinfo.cs_lat = gridinfo->ct->del.phi; + + /* bounds of grid */ + grinfo.lowerleft = gridinfo->ct->ll; + grinfo.upperright.lam = grinfo.lowerleft.lam + grinfo.n_lon*grinfo.cs_lon; + grinfo.upperright.phi = grinfo.lowerleft.phi + grinfo.n_lat*grinfo.cs_lat; + + pj_gridinfo_free(ctx, gridinfo); + + return grinfo; +} + + + +/*****************************************************************************/ +PJ_INIT_INFO proj_init_info(const char *initname){ +/****************************************************************************** + Information about a named init file. + + Maximum length of initname is 64. + + Returns PJ_INIT_INFO struct. + + If the init file is not found all members of the return struct are set + to the empty string. + + If the init file is found, but the metadata is missing, the value is + set to "Unknown". +******************************************************************************/ + int file_found; + char param[80], key[74]; + paralist *start, *next; + PJ_INIT_INFO ininfo; + PJ_CONTEXT *ctx = pj_get_default_ctx(); + + memset(&ininfo, 0, sizeof(PJ_INIT_INFO)); + + file_found = pj_find_file(ctx, initname, ininfo.filename, sizeof(ininfo.filename)); + if (!file_found || strlen(initname) > 64) { + if( strcmp(initname, "epsg") == 0 || strcmp(initname, "EPSG") == 0 ) { + const char* val; + + pj_ctx_set_errno( ctx, 0 ); + + strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strcpy(ininfo.origin, "EPSG"); + val = proj_context_get_database_metadata(ctx, "EPSG.VERSION"); + if( val ) { + strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); + } + val = proj_context_get_database_metadata(ctx, "EPSG.DATE"); + if( val ) { + strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); + } + return ininfo; + } + + if( strcmp(initname, "IGNF") == 0 ) { + const char* val; + + pj_ctx_set_errno( ctx, 0 ); + + strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strcpy(ininfo.origin, "IGNF"); + val = proj_context_get_database_metadata(ctx, "IGNF.VERSION"); + if( val ) { + strncpy(ininfo.version, val, sizeof(ininfo.version) - 1); + } + val = proj_context_get_database_metadata(ctx, "IGNF.DATE"); + if( val ) { + strncpy(ininfo.lastupdate, val, sizeof(ininfo.lastupdate) - 1); + } + return ininfo; + } + + return ininfo; + } + + /* The initial memset (0) makes strncpy safe here */ + strncpy (ininfo.name, initname, sizeof(ininfo.name) - 1); + strcpy(ininfo.origin, "Unknown"); + strcpy(ininfo.version, "Unknown"); + strcpy(ininfo.lastupdate, "Unknown"); + + strncpy (key, initname, 64); /* make room for ":metadata\0" at the end */ + key[64] = 0; + memcpy(key + strlen(key), ":metadata", 9 + 1); + strcpy(param, "+init="); + /* The +strlen(param) avoids a cppcheck false positive warning */ + strncat(param + strlen(param), key, sizeof(param)-1-strlen(param)); + + start = pj_mkparam(param); + pj_expand_init(ctx, start); + + if (pj_param(ctx, start, "tversion").i) + strncpy(ininfo.version, pj_param(ctx, start, "sversion").s, sizeof(ininfo.version) - 1); + + if (pj_param(ctx, start, "torigin").i) + strncpy(ininfo.origin, pj_param(ctx, start, "sorigin").s, sizeof(ininfo.origin) - 1); + + if (pj_param(ctx, start, "tlastupdate").i) + strncpy(ininfo.lastupdate, pj_param(ctx, start, "slastupdate").s, sizeof(ininfo.lastupdate) - 1); + + for ( ; start; start = next) { + next = start->next; + pj_dalloc(start); + } + + return ininfo; +} + + + +/*****************************************************************************/ +PJ_FACTORS proj_factors(PJ *P, PJ_COORD lp) { +/****************************************************************************** + Cartographic characteristics at point lp. + + Characteristics include meridian, parallel and areal scales, angular + distortion, meridian/parallel, meridian convergence and scale error. + + returns PJ_FACTORS. If unsuccessful, error number is set and the + struct returned contains NULL data. +******************************************************************************/ + PJ_FACTORS factors = {0,0,0, 0,0,0, 0,0, 0,0,0,0}; + struct FACTORS f; + + if (0==P) + return factors; + + if (pj_factors(lp.lp, P, 0.0, &f)) + return factors; + + factors.meridional_scale = f.h; + factors.parallel_scale = f.k; + factors.areal_scale = f.s; + + factors.angular_distortion = f.omega; + factors.meridian_parallel_angle = f.thetap; + factors.meridian_convergence = f.conv; + + factors.tissot_semimajor = f.a; + factors.tissot_semiminor = f.b; + + /* Raw derivatives, for completeness's sake */ + factors.dx_dlam = f.der.x_l; + factors.dx_dphi = f.der.x_p; + factors.dy_dlam = f.der.y_l; + factors.dy_dphi = f.der.y_p; + + return factors; +} diff --git a/src/proj_etmerc.c b/src/proj_etmerc.c deleted file mode 100644 index 4d7187e6..00000000 --- a/src/proj_etmerc.c +++ /dev/null @@ -1,360 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2008 Gerald I. Evenden -*/ - -/* -** 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. -*/ - -/* The code in this file is largly based upon procedures: - * - * Written by: Knud Poder and Karsten Engsager - * - * Based on math from: R.Koenig and K.H. Weise, "Mathematische - * Grundlagen der hoeheren Geodaesie und Kartographie, - * Springer-Verlag, Berlin/Goettingen" Heidelberg, 1951. - * - * Modified and used here by permission of Reference Networks - * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark - * -*/ - -#define PJ_LIB__ - -#include - -#include "proj.h" -#include "projects.h" -#include "proj_math.h" - - -struct pj_opaque { - double Qn; /* Merid. quad., scaled to the projection */ \ - double Zb; /* Radius vector in polar coord. systems */ \ - double cgb[6]; /* Constants for Gauss -> Geo lat */ \ - double cbg[6]; /* Constants for Geo lat -> Gauss */ \ - double utg[6]; /* Constants for transv. merc. -> geo */ \ - double gtu[6]; /* Constants for geo -> transv. merc. */ -}; - -PROJ_HEAD(etmerc, "Extended Transverse Mercator") - "\n\tCyl, Sph\n\tlat_ts=(0)\nlat_0=(0)"; -PROJ_HEAD(utm, "Universal Transverse Mercator (UTM)") - "\n\tCyl, Sph\n\tzone= south"; - -#define PROJ_ETMERC_ORDER 6 - -#ifdef _GNU_SOURCE - inline -#endif -static double gatg(double *p1, int len_p1, double B) { - double *p; - double h = 0, h1, h2 = 0, cos_2B; - - cos_2B = 2*cos(2*B); - p = p1 + len_p1; - h1 = *--p; - while (p - p1) { - h = -h2 + cos_2B*h1 + *--p; - h2 = h1; - h1 = h; - } - return (B + h*sin(2*B)); -} - -/* Complex Clenshaw summation */ -#ifdef _GNU_SOURCE - inline -#endif -static double clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { - double *p, r, i, hr, hr1, hr2, hi, hi1, hi2; - double sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i; - - /* arguments */ - p = a + size; -#ifdef _GNU_SOURCE - sincos(arg_r, &sin_arg_r, &cos_arg_r); -#else - sin_arg_r = sin(arg_r); - cos_arg_r = cos(arg_r); -#endif - sinh_arg_i = sinh(arg_i); - cosh_arg_i = cosh(arg_i); - r = 2*cos_arg_r*cosh_arg_i; - i = -2*sin_arg_r*sinh_arg_i; - - /* summation loop */ - hi1 = hr1 = hi = 0; - hr = *--p; - for (; a - p;) { - hr2 = hr1; - hi2 = hi1; - hr1 = hr; - hi1 = hi; - hr = -hr2 + r*hr1 - i*hi1 + *--p; - hi = -hi2 + i*hr1 + r*hi1; - } - - r = sin_arg_r*cosh_arg_i; - i = cos_arg_r*sinh_arg_i; - *R = r*hr - i*hi; - *I = r*hi + i*hr; - return *R; -} - - -/* Real Clenshaw summation */ -static double clens(double *a, int size, double arg_r) { - double *p, r, hr, hr1, hr2, cos_arg_r; - - p = a + size; - cos_arg_r = cos(arg_r); - r = 2*cos_arg_r; - - /* summation loop */ - hr1 = 0; - hr = *--p; - for (; a - p;) { - hr2 = hr1; - hr1 = hr; - hr = -hr2 + r*hr1 + *--p; - } - return sin (arg_r)*hr; -} - - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; - double Cn = lp.phi, Ce = lp.lam; - - /* ell. LAT, LNG -> Gaussian LAT, LNG */ - Cn = gatg (Q->cbg, PROJ_ETMERC_ORDER, Cn); - /* Gaussian LAT, LNG -> compl. sph. LAT */ -#ifdef _GNU_SOURCE - sincos (Cn, &sin_Cn, &cos_Cn); - sincos (Ce, &sin_Ce, &cos_Ce); -#else - sin_Cn = sin (Cn); - cos_Cn = cos (Cn); - sin_Ce = sin (Ce); - cos_Ce = cos (Ce); -#endif - - Cn = atan2 (sin_Cn, cos_Ce*cos_Cn); - Ce = atan2 (sin_Ce*cos_Cn, hypot (sin_Cn, cos_Cn*cos_Ce)); - - /* compl. sph. N, E -> ell. norm. N, E */ - Ce = asinh ( tan (Ce) ); /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */ - Cn += clenS (Q->gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); - Ce += dCe; - if (fabs (Ce) <= 2.623395162778) { - xy.y = Q->Qn * Cn + Q->Zb; /* Northing */ - xy.x = Q->Qn * Ce; /* Easting */ - } else - xy.x = xy.y = HUGE_VAL; - return xy; -} - - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; - double Cn = xy.y, Ce = xy.x; - - /* normalize N, E */ - Cn = (Cn - Q->Zb)/Q->Qn; - Ce = Ce/Q->Qn; - - if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */ - /* norm. N, E -> compl. sph. LAT, LNG */ - Cn += clenS(Q->utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); - Ce += dCe; - Ce = atan (sinh (Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - FORTPI); */ - /* compl. sph. LAT -> Gaussian LAT, LNG */ -#ifdef _GNU_SOURCE - sincos (Cn, &sin_Cn, &cos_Cn); - sincos (Ce, &sin_Ce, &cos_Ce); -#else - sin_Cn = sin (Cn); - cos_Cn = cos (Cn); - sin_Ce = sin (Ce); - cos_Ce = cos (Ce); -#endif - Ce = atan2 (sin_Ce, cos_Ce*cos_Cn); - Cn = atan2 (sin_Cn*cos_Ce, hypot (sin_Ce, cos_Ce*cos_Cn)); - /* Gaussian LAT, LNG -> ell. LAT, LNG */ - lp.phi = gatg (Q->cgb, PROJ_ETMERC_ORDER, Cn); - lp.lam = Ce; - } - else - lp.phi = lp.lam = HUGE_VAL; - return lp; -} - - -static PJ *setup(PJ *P) { /* general initialization */ - double f, n, np, Z; - struct pj_opaque *Q = P->opaque; - - if (P->es <= 0) { - return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); - } - - /* flattening */ - f = P->es / (1 + sqrt (1 - P->es)); /* Replaces: f = 1 - sqrt(1-P->es); */ - - /* third flattening */ - np = n = f/(2 - f); - - /* COEF. OF TRIG SERIES GEO <-> GAUSS */ - /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */ - /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */ - /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */ - - Q->cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 + - n*(-2854/675.0 )))))); - Q->cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 + - n*( 4642/4725.0)))))); - np *= n; - Q->cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 + - n*( 2323/945.0))))); - Q->cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 + - n*(-1522/945.0))))); - np *= n; - /* n^5 coeff corrected from 1262/105 -> -1262/105 */ - Q->cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 + - n*( 73814/2835.0)))); - Q->cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 + - n*(-12686/2835.0)))); - np *= n; - /* n^5 coeff corrected from 322/35 -> 332/35 */ - Q->cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0))); - Q->cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0))); - np *= n; - Q->cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 )); - Q->cbg[4] = np*(-734/315.0 + n*( 109598/31185.0)); - np *= n; - Q->cgb[5] = np*(601676/22275.0 ); - Q->cbg[5] = np*(444337/155925.0); - - /* Constants of the projections */ - /* Transverse Mercator (UTM, ITM, etc) */ - np = n*n; - /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */ - Q->Qn = P->k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0))); - /* coef of trig series */ - /* utg := ell. N, E -> sph. N, E, KW p194 (65) */ - /* gtu := sph. N, E -> ell. N, E, KW p196 (69) */ - Q->utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 + - n*( 81/512.0 + n*(-96199/604800.0)))))); - Q->gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 + - n*(-127/288.0 + n*( 7891/37800.0 )))))); - Q->utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 + - n*( 1118711/3870720.0))))); - Q->gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 + - n*(-1983433/1935360.0))))); - np *= n; - Q->utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 + - n*( -5569/90720.0 )))); - Q->gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 + - n*(167603/181440.0)))); - np *= n; - Q->utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0))); - Q->gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0))); - np *= n; - Q->utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0)); - Q->gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0)); - np *= n; - Q->utg[5] = np*(-20648693/638668800.0); - Q->gtu[5] = np*(212378941/319334400.0); - - /* Gaussian latitude value of the origin latitude */ - Z = gatg (Q->cbg, PROJ_ETMERC_ORDER, P->phi0); - - /* Origin northing minus true northing at the origin latitude */ - /* i.e. true northing = N - P->Zb */ - Q->Zb = - Q->Qn*(Z + clens(Q->gtu, PROJ_ETMERC_ORDER, 2*Z)); - P->inv = e_inverse; - P->fwd = e_forward; - return P; -} - - - -PJ *PROJECTION(etmerc) { - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - return setup (P); -} - - - -/* utm uses etmerc for the underlying projection */ - - -PJ *PROJECTION(utm) { - long zone; - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor (P, ENOMEM); - P->opaque = Q; - - if (P->es == 0.0) { - proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); - return pj_default_destructor(P, ENOMEM); - } - if (P->lam0 < -1000.0 || P->lam0 > 1000.0) { - return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE); - } - - P->y0 = pj_param (P->ctx, P->params, "bsouth").i ? 10000000. : 0.; - P->x0 = 500000.; - if (pj_param (P->ctx, P->params, "tzone").i) /* zone input ? */ - { - zone = pj_param(P->ctx, P->params, "izone").i; - if (zone > 0 && zone <= 60) - --zone; - else { - return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE); - } - } - else /* nearest central meridian input */ - { - zone = lround((floor ((adjlon (P->lam0) + M_PI) * 30. / M_PI))); - if (zone < 0) - zone = 0; - else if (zone >= 60) - zone = 59; - } - P->lam0 = (zone + .5) * M_PI / 30. - M_PI; - P->k0 = 0.9996; - P->phi0 = 0.; - - return setup (P); -} diff --git a/src/proj_etmerc.cpp b/src/proj_etmerc.cpp new file mode 100644 index 00000000..0ba710d7 --- /dev/null +++ b/src/proj_etmerc.cpp @@ -0,0 +1,360 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2008 Gerald I. Evenden +*/ + +/* +** 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. +*/ + +/* The code in this file is largly based upon procedures: + * + * Written by: Knud Poder and Karsten Engsager + * + * Based on math from: R.Koenig and K.H. Weise, "Mathematische + * Grundlagen der hoeheren Geodaesie und Kartographie, + * Springer-Verlag, Berlin/Goettingen" Heidelberg, 1951. + * + * Modified and used here by permission of Reference Networks + * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark + * +*/ + +#define PJ_LIB__ + +#include + +#include "proj.h" +#include "projects.h" +#include "proj_math.h" + + +struct pj_opaque { + double Qn; /* Merid. quad., scaled to the projection */ \ + double Zb; /* Radius vector in polar coord. systems */ \ + double cgb[6]; /* Constants for Gauss -> Geo lat */ \ + double cbg[6]; /* Constants for Geo lat -> Gauss */ \ + double utg[6]; /* Constants for transv. merc. -> geo */ \ + double gtu[6]; /* Constants for geo -> transv. merc. */ +}; + +PROJ_HEAD(etmerc, "Extended Transverse Mercator") + "\n\tCyl, Sph\n\tlat_ts=(0)\nlat_0=(0)"; +PROJ_HEAD(utm, "Universal Transverse Mercator (UTM)") + "\n\tCyl, Sph\n\tzone= south"; + +#define PROJ_ETMERC_ORDER 6 + +#ifdef _GNU_SOURCE + inline +#endif +static double gatg(double *p1, int len_p1, double B) { + double *p; + double h = 0, h1, h2 = 0, cos_2B; + + cos_2B = 2*cos(2*B); + p = p1 + len_p1; + h1 = *--p; + while (p - p1) { + h = -h2 + cos_2B*h1 + *--p; + h2 = h1; + h1 = h; + } + return (B + h*sin(2*B)); +} + +/* Complex Clenshaw summation */ +#ifdef _GNU_SOURCE + inline +#endif +static double clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { + double *p, r, i, hr, hr1, hr2, hi, hi1, hi2; + double sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i; + + /* arguments */ + p = a + size; +#ifdef _GNU_SOURCE + sincos(arg_r, &sin_arg_r, &cos_arg_r); +#else + sin_arg_r = sin(arg_r); + cos_arg_r = cos(arg_r); +#endif + sinh_arg_i = sinh(arg_i); + cosh_arg_i = cosh(arg_i); + r = 2*cos_arg_r*cosh_arg_i; + i = -2*sin_arg_r*sinh_arg_i; + + /* summation loop */ + hi1 = hr1 = hi = 0; + hr = *--p; + for (; a - p;) { + hr2 = hr1; + hi2 = hi1; + hr1 = hr; + hi1 = hi; + hr = -hr2 + r*hr1 - i*hi1 + *--p; + hi = -hi2 + i*hr1 + r*hi1; + } + + r = sin_arg_r*cosh_arg_i; + i = cos_arg_r*sinh_arg_i; + *R = r*hr - i*hi; + *I = r*hi + i*hr; + return *R; +} + + +/* Real Clenshaw summation */ +static double clens(double *a, int size, double arg_r) { + double *p, r, hr, hr1, hr2, cos_arg_r; + + p = a + size; + cos_arg_r = cos(arg_r); + r = 2*cos_arg_r; + + /* summation loop */ + hr1 = 0; + hr = *--p; + for (; a - p;) { + hr2 = hr1; + hr1 = hr; + hr = -hr2 + r*hr1 + *--p; + } + return sin (arg_r)*hr; +} + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; + double Cn = lp.phi, Ce = lp.lam; + + /* ell. LAT, LNG -> Gaussian LAT, LNG */ + Cn = gatg (Q->cbg, PROJ_ETMERC_ORDER, Cn); + /* Gaussian LAT, LNG -> compl. sph. LAT */ +#ifdef _GNU_SOURCE + sincos (Cn, &sin_Cn, &cos_Cn); + sincos (Ce, &sin_Ce, &cos_Ce); +#else + sin_Cn = sin (Cn); + cos_Cn = cos (Cn); + sin_Ce = sin (Ce); + cos_Ce = cos (Ce); +#endif + + Cn = atan2 (sin_Cn, cos_Ce*cos_Cn); + Ce = atan2 (sin_Ce*cos_Cn, hypot (sin_Cn, cos_Cn*cos_Ce)); + + /* compl. sph. N, E -> ell. norm. N, E */ + Ce = asinh ( tan (Ce) ); /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */ + Cn += clenS (Q->gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); + Ce += dCe; + if (fabs (Ce) <= 2.623395162778) { + xy.y = Q->Qn * Cn + Q->Zb; /* Northing */ + xy.x = Q->Qn * Ce; /* Easting */ + } else + xy.x = xy.y = HUGE_VAL; + return xy; +} + + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; + double Cn = xy.y, Ce = xy.x; + + /* normalize N, E */ + Cn = (Cn - Q->Zb)/Q->Qn; + Ce = Ce/Q->Qn; + + if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */ + /* norm. N, E -> compl. sph. LAT, LNG */ + Cn += clenS(Q->utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); + Ce += dCe; + Ce = atan (sinh (Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - FORTPI); */ + /* compl. sph. LAT -> Gaussian LAT, LNG */ +#ifdef _GNU_SOURCE + sincos (Cn, &sin_Cn, &cos_Cn); + sincos (Ce, &sin_Ce, &cos_Ce); +#else + sin_Cn = sin (Cn); + cos_Cn = cos (Cn); + sin_Ce = sin (Ce); + cos_Ce = cos (Ce); +#endif + Ce = atan2 (sin_Ce, cos_Ce*cos_Cn); + Cn = atan2 (sin_Cn*cos_Ce, hypot (sin_Ce, cos_Ce*cos_Cn)); + /* Gaussian LAT, LNG -> ell. LAT, LNG */ + lp.phi = gatg (Q->cgb, PROJ_ETMERC_ORDER, Cn); + lp.lam = Ce; + } + else + lp.phi = lp.lam = HUGE_VAL; + return lp; +} + + +static PJ *setup(PJ *P) { /* general initialization */ + double f, n, np, Z; + struct pj_opaque *Q = static_cast(P->opaque); + + if (P->es <= 0) { + return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); + } + + /* flattening */ + f = P->es / (1 + sqrt (1 - P->es)); /* Replaces: f = 1 - sqrt(1-P->es); */ + + /* third flattening */ + np = n = f/(2 - f); + + /* COEF. OF TRIG SERIES GEO <-> GAUSS */ + /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */ + /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */ + /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */ + + Q->cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 + + n*(-2854/675.0 )))))); + Q->cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 + + n*( 4642/4725.0)))))); + np *= n; + Q->cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 + + n*( 2323/945.0))))); + Q->cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 + + n*(-1522/945.0))))); + np *= n; + /* n^5 coeff corrected from 1262/105 -> -1262/105 */ + Q->cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 + + n*( 73814/2835.0)))); + Q->cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 + + n*(-12686/2835.0)))); + np *= n; + /* n^5 coeff corrected from 322/35 -> 332/35 */ + Q->cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0))); + Q->cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0))); + np *= n; + Q->cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 )); + Q->cbg[4] = np*(-734/315.0 + n*( 109598/31185.0)); + np *= n; + Q->cgb[5] = np*(601676/22275.0 ); + Q->cbg[5] = np*(444337/155925.0); + + /* Constants of the projections */ + /* Transverse Mercator (UTM, ITM, etc) */ + np = n*n; + /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */ + Q->Qn = P->k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0))); + /* coef of trig series */ + /* utg := ell. N, E -> sph. N, E, KW p194 (65) */ + /* gtu := sph. N, E -> ell. N, E, KW p196 (69) */ + Q->utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 + + n*( 81/512.0 + n*(-96199/604800.0)))))); + Q->gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 + + n*(-127/288.0 + n*( 7891/37800.0 )))))); + Q->utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 + + n*( 1118711/3870720.0))))); + Q->gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 + + n*(-1983433/1935360.0))))); + np *= n; + Q->utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 + + n*( -5569/90720.0 )))); + Q->gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 + + n*(167603/181440.0)))); + np *= n; + Q->utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0))); + Q->gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0))); + np *= n; + Q->utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0)); + Q->gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0)); + np *= n; + Q->utg[5] = np*(-20648693/638668800.0); + Q->gtu[5] = np*(212378941/319334400.0); + + /* Gaussian latitude value of the origin latitude */ + Z = gatg (Q->cbg, PROJ_ETMERC_ORDER, P->phi0); + + /* Origin northing minus true northing at the origin latitude */ + /* i.e. true northing = N - P->Zb */ + Q->Zb = - Q->Qn*(Z + clens(Q->gtu, PROJ_ETMERC_ORDER, 2*Z)); + P->inv = e_inverse; + P->fwd = e_forward; + return P; +} + + + +PJ *PROJECTION(etmerc) { + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + return setup (P); +} + + + +/* utm uses etmerc for the underlying projection */ + + +PJ *PROJECTION(utm) { + long zone; + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor (P, ENOMEM); + P->opaque = Q; + + if (P->es == 0.0) { + proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED); + return pj_default_destructor(P, ENOMEM); + } + if (P->lam0 < -1000.0 || P->lam0 > 1000.0) { + return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE); + } + + P->y0 = pj_param (P->ctx, P->params, "bsouth").i ? 10000000. : 0.; + P->x0 = 500000.; + if (pj_param (P->ctx, P->params, "tzone").i) /* zone input ? */ + { + zone = pj_param(P->ctx, P->params, "izone").i; + if (zone > 0 && zone <= 60) + --zone; + else { + return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE); + } + } + else /* nearest central meridian input */ + { + zone = lround((floor ((adjlon (P->lam0) + M_PI) * 30. / M_PI))); + if (zone < 0) + zone = 0; + else if (zone >= 60) + zone = 59; + } + P->lam0 = (zone + .5) * M_PI / 30. - M_PI; + P->k0 = 0.9996; + P->phi0 = 0.; + + return setup (P); +} diff --git a/src/proj_mdist.c b/src/proj_mdist.c deleted file mode 100644 index 777f704d..00000000 --- a/src/proj_mdist.c +++ /dev/null @@ -1,127 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2003, 2006 Gerald I. Evenden -*/ -/* -** 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. -*/ -/* Computes distance from equator along the meridian to latitude phi -** and inverse on unit ellipsoid. -** Precision commensurate with double precision. -*/ -#define PJ_LIB__ - -#include -#include - -#include "projects.h" - -#define MAX_ITER 20 -#define TOL 1e-14 - -struct MDIST { - int nb; - double es; - double E; - double b[1]; -}; - void * -proj_mdist_ini(double es) { - double numf, numfi, twon1, denf, denfi, ens, T, twon; - double den, El, Es; - double E[MAX_ITER]; - struct MDIST *b; - int i, j; - -/* generate E(e^2) and its terms E[] */ - ens = es; - numf = twon1 = denfi = 1.; - denf = 1.; - twon = 4.; - Es = El = E[0] = 1.; - for (i = 1; i < MAX_ITER ; ++i) { - numf *= (twon1 * twon1); - den = twon * denf * denf * twon1; - T = numf/den; - Es -= (E[i] = T * ens); - ens *= es; - twon *= 4.; - denf *= ++denfi; - twon1 += 2.; - if (Es == El) /* jump out if no change */ - break; - El = Es; - } - if ((b = (struct MDIST *)malloc(sizeof(struct MDIST)+ - (i*sizeof(double)))) == NULL) - return(NULL); - b->nb = i - 1; - b->es = es; - b->E = Es; - /* generate b_n coefficients--note: collapse with prefix ratios */ - b->b[0] = Es = 1. - Es; - numf = denf = 1.; - numfi = 2.; - denfi = 3.; - for (j = 1; j < i; ++j) { - Es -= E[j]; - numf *= numfi; - denf *= denfi; - b->b[j] = Es * numf / denf; - numfi += 2.; - denfi += 2.; - } - return (b); -} - double -proj_mdist(double phi, double sphi, double cphi, const void *data) { - const struct MDIST *b = (const struct MDIST *)data; - double sc, sum, sphi2, D; - int i; - - sc = sphi * cphi; - sphi2 = sphi * sphi; - D = phi * b->E - b->es * sc / sqrt(1. - b->es * sphi2); - sum = b->b[i = b->nb]; - while (i) sum = b->b[--i] + sphi2 * sum; - return(D + sc * sum); -} - double -proj_inv_mdist(projCtx ctx, double dist, const void *data) { - const struct MDIST *b = (const struct MDIST *)data; - double s, t, phi, k; - int i; - - k = 1./(1.- b->es); - i = MAX_ITER; - phi = dist; - while ( i-- ) { - s = sin(phi); - t = 1. - b->es * s * s; - phi -= t = (proj_mdist(phi, s, cos(phi), b) - dist) * - (t * sqrt(t)) * k; - if (fabs(t) < TOL) /* that is no change */ - return phi; - } - /* convergence failed */ - pj_ctx_set_errno(ctx, PJD_ERR_NON_CONV_INV_MERI_DIST); - return phi; -} diff --git a/src/proj_mdist.cpp b/src/proj_mdist.cpp new file mode 100644 index 00000000..777f704d --- /dev/null +++ b/src/proj_mdist.cpp @@ -0,0 +1,127 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2003, 2006 Gerald I. Evenden +*/ +/* +** 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. +*/ +/* Computes distance from equator along the meridian to latitude phi +** and inverse on unit ellipsoid. +** Precision commensurate with double precision. +*/ +#define PJ_LIB__ + +#include +#include + +#include "projects.h" + +#define MAX_ITER 20 +#define TOL 1e-14 + +struct MDIST { + int nb; + double es; + double E; + double b[1]; +}; + void * +proj_mdist_ini(double es) { + double numf, numfi, twon1, denf, denfi, ens, T, twon; + double den, El, Es; + double E[MAX_ITER]; + struct MDIST *b; + int i, j; + +/* generate E(e^2) and its terms E[] */ + ens = es; + numf = twon1 = denfi = 1.; + denf = 1.; + twon = 4.; + Es = El = E[0] = 1.; + for (i = 1; i < MAX_ITER ; ++i) { + numf *= (twon1 * twon1); + den = twon * denf * denf * twon1; + T = numf/den; + Es -= (E[i] = T * ens); + ens *= es; + twon *= 4.; + denf *= ++denfi; + twon1 += 2.; + if (Es == El) /* jump out if no change */ + break; + El = Es; + } + if ((b = (struct MDIST *)malloc(sizeof(struct MDIST)+ + (i*sizeof(double)))) == NULL) + return(NULL); + b->nb = i - 1; + b->es = es; + b->E = Es; + /* generate b_n coefficients--note: collapse with prefix ratios */ + b->b[0] = Es = 1. - Es; + numf = denf = 1.; + numfi = 2.; + denfi = 3.; + for (j = 1; j < i; ++j) { + Es -= E[j]; + numf *= numfi; + denf *= denfi; + b->b[j] = Es * numf / denf; + numfi += 2.; + denfi += 2.; + } + return (b); +} + double +proj_mdist(double phi, double sphi, double cphi, const void *data) { + const struct MDIST *b = (const struct MDIST *)data; + double sc, sum, sphi2, D; + int i; + + sc = sphi * cphi; + sphi2 = sphi * sphi; + D = phi * b->E - b->es * sc / sqrt(1. - b->es * sphi2); + sum = b->b[i = b->nb]; + while (i) sum = b->b[--i] + sphi2 * sum; + return(D + sc * sum); +} + double +proj_inv_mdist(projCtx ctx, double dist, const void *data) { + const struct MDIST *b = (const struct MDIST *)data; + double s, t, phi, k; + int i; + + k = 1./(1.- b->es); + i = MAX_ITER; + phi = dist; + while ( i-- ) { + s = sin(phi); + t = 1. - b->es * s * s; + phi -= t = (proj_mdist(phi, s, cos(phi), b) - dist) * + (t * sqrt(t)) * k; + if (fabs(t) < TOL) /* that is no change */ + return phi; + } + /* convergence failed */ + pj_ctx_set_errno(ctx, PJD_ERR_NON_CONV_INV_MERI_DIST); + return phi; +} diff --git a/src/proj_rouss.c b/src/proj_rouss.c deleted file mode 100644 index 0e0f9982..00000000 --- a/src/proj_rouss.c +++ /dev/null @@ -1,156 +0,0 @@ -/* -** libproj -- library of cartographic projections -** -** Copyright (c) 2003, 2006 Gerald I. Evenden -*/ -/* -** 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. -*/ -#define PJ_LIB__ - -#include -#include - -#include "proj.h" -#include "projects.h" - -struct pj_opaque { - double s0; - double A1, A2, A3, A4, A5, A6; - double B1, B2, B3, B4, B5, B6, B7, B8; - double C1, C2, C3, C4, C5, C6, C7, C8; - double D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11; - void *en; -}; -PROJ_HEAD(rouss, "Roussilhe Stereographic") "\n\tAzi, Ell"; - - -static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ - XY xy = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double s, al, cp, sp, al2, s2; - - cp = cos(lp.phi); - sp = sin(lp.phi); - s = proj_mdist(lp.phi, sp, cp, Q->en) - Q->s0; - s2 = s * s; - al = lp.lam * cp / sqrt(1. - P->es * sp * sp); - al2 = al * al; - xy.x = P->k0 * al*(1.+s2*(Q->A1+s2*Q->A4)-al2*(Q->A2+s*Q->A3+s2*Q->A5 - +al2*Q->A6)); - xy.y = P->k0 * (al2*(Q->B1+al2*Q->B4)+ - s*(1.+al2*(Q->B3-al2*Q->B6)+s2*(Q->B2+s2*Q->B8)+ - s*al2*(Q->B5+s*Q->B7))); - - return xy; -} - - -static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ - LP lp = {0.0,0.0}; - struct pj_opaque *Q = P->opaque; - double s, al, x = xy.x / P->k0, y = xy.y / P->k0, x2, y2;; - - x2 = x * x; - y2 = y * y; - al = x*(1.-Q->C1*y2+x2*(Q->C2+Q->C3*y-Q->C4*x2+Q->C5*y2-Q->C7*x2*y) - +y2*(Q->C6*y2-Q->C8*x2*y)); - s = Q->s0 + y*(1.+y2*(-Q->D2+Q->D8*y2))+ - x2*(-Q->D1+y*(-Q->D3+y*(-Q->D5+y*(-Q->D7+y*Q->D11)))+ - x2*(Q->D4+y*(Q->D6+y*Q->D10)-x2*Q->D9)); - lp.phi=proj_inv_mdist(P->ctx, s, Q->en); - s = sin(lp.phi); - lp.lam=al * sqrt(1. - P->es * s * s)/cos(lp.phi); - - return lp; -} - - -static void *destructor (PJ *P, int errlev) { - if (0==P) - return 0; - - if (0==P->opaque) - return pj_default_destructor (P, errlev); - - if (P->opaque->en) - pj_dealloc (P->opaque->en); - - return pj_default_destructor (P, ENOMEM); -} - - -PJ *PROJECTION(rouss) { - double N0, es2, t, t2, R_R0_2, R_R0_4; - - struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); - if (0==Q) - return pj_default_destructor(P, ENOMEM); - P->opaque = Q; - - if (!((Q->en = proj_mdist_ini(P->es)))) - return pj_default_destructor (P, ENOMEM); - - es2 = sin(P->phi0); - Q->s0 = proj_mdist(P->phi0, es2, cos(P->phi0), Q->en); - t = 1. - (es2 = P->es * es2 * es2); - N0 = 1./sqrt(t); - R_R0_2 = t * t / P->one_es; - R_R0_4 = R_R0_2 * R_R0_2; - t = tan(P->phi0); - t2 = t * t; - Q->C1 = Q->A1 = R_R0_2 / 4.; - Q->C2 = Q->A2 = R_R0_2 * (2 * t2 - 1. - 2. * es2) / 12.; - Q->A3 = R_R0_2 * t * (1. + 4. * t2)/ ( 12. * N0); - Q->A4 = R_R0_4 / 24.; - Q->A5 = R_R0_4 * ( -1. + t2 * (11. + 12. * t2))/24.; - Q->A6 = R_R0_4 * ( -2. + t2 * (11. - 2. * t2))/240.; - Q->B1 = t / (2. * N0); - Q->B2 = R_R0_2 / 12.; - Q->B3 = R_R0_2 * (1. + 2. * t2 - 2. * es2)/4.; - Q->B4 = R_R0_2 * t * (2. - t2)/(24. * N0); - Q->B5 = R_R0_2 * t * (5. + 4.* t2)/(8. * N0); - Q->B6 = R_R0_4 * (-2. + t2 * (-5. + 6. * t2))/48.; - Q->B7 = R_R0_4 * (5. + t2 * (19. + 12. * t2))/24.; - Q->B8 = R_R0_4 / 120.; - Q->C3 = R_R0_2 * t * (1. + t2)/(3. * N0); - Q->C4 = R_R0_4 * (-3. + t2 * (34. + 22. * t2))/240.; - Q->C5 = R_R0_4 * (4. + t2 * (13. + 12. * t2))/24.; - Q->C6 = R_R0_4 / 16.; - Q->C7 = R_R0_4 * t * (11. + t2 * (33. + t2 * 16.))/(48. * N0); - Q->C8 = R_R0_4 * t * (1. + t2 * 4.)/(36. * N0); - Q->D1 = t / (2. * N0); - Q->D2 = R_R0_2 / 12.; - Q->D3 = R_R0_2 * (2 * t2 + 1. - 2. * es2) / 4.; - Q->D4 = R_R0_2 * t * (1. + t2)/(8. * N0); - Q->D5 = R_R0_2 * t * (1. + t2 * 2.)/(4. * N0); - Q->D6 = R_R0_4 * (1. + t2 * (6. + t2 * 6.))/16.; - Q->D7 = R_R0_4 * t2 * (3. + t2 * 4.)/8.; - Q->D8 = R_R0_4 / 80.; - Q->D9 = R_R0_4 * t * (-21. + t2 * (178. - t2 * 26.))/720.; - Q->D10 = R_R0_4 * t * (29. + t2 * (86. + t2 * 48.))/(96. * N0); - Q->D11 = R_R0_4 * t * (37. + t2 * 44.)/(96. * N0); - - P->fwd = e_forward; - P->inv = e_inverse; - P->destructor = destructor; - - return P; -} diff --git a/src/proj_rouss.cpp b/src/proj_rouss.cpp new file mode 100644 index 00000000..f39e0a15 --- /dev/null +++ b/src/proj_rouss.cpp @@ -0,0 +1,156 @@ +/* +** libproj -- library of cartographic projections +** +** Copyright (c) 2003, 2006 Gerald I. Evenden +*/ +/* +** 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. +*/ +#define PJ_LIB__ + +#include +#include + +#include "proj.h" +#include "projects.h" + +struct pj_opaque { + double s0; + double A1, A2, A3, A4, A5, A6; + double B1, B2, B3, B4, B5, B6, B7, B8; + double C1, C2, C3, C4, C5, C6, C7, C8; + double D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11; + void *en; +}; +PROJ_HEAD(rouss, "Roussilhe Stereographic") "\n\tAzi, Ell"; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double s, al, cp, sp, al2, s2; + + cp = cos(lp.phi); + sp = sin(lp.phi); + s = proj_mdist(lp.phi, sp, cp, Q->en) - Q->s0; + s2 = s * s; + al = lp.lam * cp / sqrt(1. - P->es * sp * sp); + al2 = al * al; + xy.x = P->k0 * al*(1.+s2*(Q->A1+s2*Q->A4)-al2*(Q->A2+s*Q->A3+s2*Q->A5 + +al2*Q->A6)); + xy.y = P->k0 * (al2*(Q->B1+al2*Q->B4)+ + s*(1.+al2*(Q->B3-al2*Q->B6)+s2*(Q->B2+s2*Q->B8)+ + s*al2*(Q->B5+s*Q->B7))); + + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = static_cast(P->opaque); + double s, al, x = xy.x / P->k0, y = xy.y / P->k0, x2, y2;; + + x2 = x * x; + y2 = y * y; + al = x*(1.-Q->C1*y2+x2*(Q->C2+Q->C3*y-Q->C4*x2+Q->C5*y2-Q->C7*x2*y) + +y2*(Q->C6*y2-Q->C8*x2*y)); + s = Q->s0 + y*(1.+y2*(-Q->D2+Q->D8*y2))+ + x2*(-Q->D1+y*(-Q->D3+y*(-Q->D5+y*(-Q->D7+y*Q->D11)))+ + x2*(Q->D4+y*(Q->D6+y*Q->D10)-x2*Q->D9)); + lp.phi=proj_inv_mdist(P->ctx, s, Q->en); + s = sin(lp.phi); + lp.lam=al * sqrt(1. - P->es * s * s)/cos(lp.phi); + + return lp; +} + + +static PJ *destructor (PJ *P, int errlev) { + if (0==P) + return 0; + + if (0==P->opaque) + return pj_default_destructor (P, errlev); + + if (static_cast(P->opaque)->en) + pj_dealloc (static_cast(P->opaque)->en); + + return pj_default_destructor (P, ENOMEM); +} + + +PJ *PROJECTION(rouss) { + double N0, es2, t, t2, R_R0_2, R_R0_4; + + struct pj_opaque *Q = static_cast(pj_calloc (1, sizeof (struct pj_opaque))); + if (0==Q) + return pj_default_destructor(P, ENOMEM); + P->opaque = Q; + + if (!((Q->en = proj_mdist_ini(P->es)))) + return pj_default_destructor (P, ENOMEM); + + es2 = sin(P->phi0); + Q->s0 = proj_mdist(P->phi0, es2, cos(P->phi0), Q->en); + t = 1. - (es2 = P->es * es2 * es2); + N0 = 1./sqrt(t); + R_R0_2 = t * t / P->one_es; + R_R0_4 = R_R0_2 * R_R0_2; + t = tan(P->phi0); + t2 = t * t; + Q->C1 = Q->A1 = R_R0_2 / 4.; + Q->C2 = Q->A2 = R_R0_2 * (2 * t2 - 1. - 2. * es2) / 12.; + Q->A3 = R_R0_2 * t * (1. + 4. * t2)/ ( 12. * N0); + Q->A4 = R_R0_4 / 24.; + Q->A5 = R_R0_4 * ( -1. + t2 * (11. + 12. * t2))/24.; + Q->A6 = R_R0_4 * ( -2. + t2 * (11. - 2. * t2))/240.; + Q->B1 = t / (2. * N0); + Q->B2 = R_R0_2 / 12.; + Q->B3 = R_R0_2 * (1. + 2. * t2 - 2. * es2)/4.; + Q->B4 = R_R0_2 * t * (2. - t2)/(24. * N0); + Q->B5 = R_R0_2 * t * (5. + 4.* t2)/(8. * N0); + Q->B6 = R_R0_4 * (-2. + t2 * (-5. + 6. * t2))/48.; + Q->B7 = R_R0_4 * (5. + t2 * (19. + 12. * t2))/24.; + Q->B8 = R_R0_4 / 120.; + Q->C3 = R_R0_2 * t * (1. + t2)/(3. * N0); + Q->C4 = R_R0_4 * (-3. + t2 * (34. + 22. * t2))/240.; + Q->C5 = R_R0_4 * (4. + t2 * (13. + 12. * t2))/24.; + Q->C6 = R_R0_4 / 16.; + Q->C7 = R_R0_4 * t * (11. + t2 * (33. + t2 * 16.))/(48. * N0); + Q->C8 = R_R0_4 * t * (1. + t2 * 4.)/(36. * N0); + Q->D1 = t / (2. * N0); + Q->D2 = R_R0_2 / 12.; + Q->D3 = R_R0_2 * (2 * t2 + 1. - 2. * es2) / 4.; + Q->D4 = R_R0_2 * t * (1. + t2)/(8. * N0); + Q->D5 = R_R0_2 * t * (1. + t2 * 2.)/(4. * N0); + Q->D6 = R_R0_4 * (1. + t2 * (6. + t2 * 6.))/16.; + Q->D7 = R_R0_4 * t2 * (3. + t2 * 4.)/8.; + Q->D8 = R_R0_4 / 80.; + Q->D9 = R_R0_4 * t * (-21. + t2 * (178. - t2 * 26.))/720.; + Q->D10 = R_R0_4 * t * (29. + t2 * (86. + t2 * 48.))/(96. * N0); + Q->D11 = R_R0_4 * t * (37. + t2 * 44.)/(96. * N0); + + P->fwd = e_forward; + P->inv = e_inverse; + P->destructor = destructor; + + return P; +} diff --git a/src/proj_strtod.c b/src/proj_strtod.c deleted file mode 100644 index a3bc7d40..00000000 --- a/src/proj_strtod.c +++ /dev/null @@ -1,440 +0,0 @@ -/*********************************************************************** - - proj_strtod: Convert string to double, accepting underscore separators - - Thomas Knudsen, 2017-01-17/09-19 - -************************************************************************ - -Conventionally, PROJ.4 does not honor locale settings, consistently -behaving as if LC_ALL=C. - -For this to work, we have, for many years, been using other solutions -than the C standard library strtod/atof functions for converting strings -to doubles. - -In the early versions of proj, iirc, a gnu version of strtod was used, -mostly to work around cases where the same system library was used for -C and Fortran linking, hence making strtod accept "D" and "d" as -exponentiation indicators, following Fortran Double Precision constant -syntax. This broke the proj angular syntax, accepting a "d" to mean -"degree": 12d34'56", meaning 12 degrees 34 minutes and 56 seconds. - -With an explicit MIT licence, PROJ.4 could not include GPL code any -longer, and apparently at some time, the GPL code was replaced by the -current C port of a GDAL function (in pj_strtod.c), which reads the -LC_NUMERIC setting and, behind the back of the user, momentarily changes -the conventional '.' delimiter to whatever the locale requires, then -calls the system supplied strtod. - -While this requires a minimum amount of coding, it only solves one -problem, and not in a very generic way. - -Another problem, I would like to see solved, is the handling of underscores -as generic delimiters. This is getting popular in a number of programming -languages (Ada, C++, C#, D, Java, Julia, Perl 5, Python, Rust, etc. -cf. e.g. https://www.python.org/dev/peps/pep-0515/), and in our case of -handling numbers being in the order of magnitude of the Earth's dimensions, -and a resolution of submillimetre, i.e. having 10 or more significant digits, -splitting the "wall of digits" into smaller chunks is of immense value. - -Hence this reimplementation of strtod, which hardcodes '.' as indicator of -numeric fractions, and accepts '_' anywhere in a numerical string sequence: -So a typical northing value can be written - - 6_098_907.8250 m -rather than - 6098907.8250 m - -which, in my humble opinion, is well worth the effort. - -While writing this code, I took ample inspiration from Michael Ringgaard's -strtod version over at http://www.jbox.dk/sanos/source/lib/strtod.c.html, -and Yasuhiro Matsumoto's public domain version over at -https://gist.github.com/mattn/1890186. The code below is, however, not -copied from any of the two mentioned - it is a reimplementation, and -probably suffers from its own set of bugs. So for now, it is intended -not as a replacement of pj_strtod, but only as an experimental piece of -code for use in an experimental new transformation program, cct. - -************************************************************************ - -Thomas Knudsen, thokn@sdfe.dk, 2017-01-17/2017-09-18 - -************************************************************************ - -* Copyright (c) 2017 Thomas Knudsen & SDFE -* -* 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 "proj_strtod.h" - -#include /* for abs */ -#include /* for strchr */ -#include -#include -#include /* for HUGE_VAL */ -#include /* for pow() */ - - -double proj_strtod(const char *str, char **endptr) { - double number = 0, integral_part = 0; - int exponent = 0; - int fraction_is_nonzero = 0; - int sign = 0; - char *p = (char *) str; - int n = 0; - int num_digits_total = 0; - int num_digits_after_comma = 0; - int num_prefixed_zeros = 0; - - if (0==str) { - errno = EFAULT; - if (endptr) - *endptr = p; - return HUGE_VAL; - } - - /* First skip leading whitespace */ - while (isspace(*p)) - p++; - - /* Empty string? */ - if (0==*p) { - if (endptr) - *endptr = (char *) str; - return 0; - } - - /* non-numeric? */ - if (0==strchr("0123456789+-._", *p)) { - if (endptr) - *endptr = (char *) str; - return 0; - } - - /* Then handle optional prefixed sign and skip prefix zeros */ - switch (*p) { - case '-': - sign = -1; - p++; - break; - case '+': - sign = 1; - p++; - break; - default: - if (isdigit(*p) || '_'==*p || '.'==*p) - break; - if (endptr) - *endptr = (char *) str; - return 0; - } - - /* stray sign, as in "+/-"? */ - if (0!=sign && (0==strchr ("0123456789._", *p) || 0==*p)) { - if (endptr) - *endptr = (char *) str; - return 0; - } - - /* skip prefixed zeros before '.' */ - while ('0'==*p || '_'==*p) - p++; - - /* zero? */ - if ((0==*p) || 0==strchr ("0123456789eE.", *p) || isspace(*p)) { - if (endptr) - *endptr = p; - return sign==-1? -0: 0; - } - - /* Now expect a (potentially zero-length) string of digits */ - while (isdigit(*p) || ('_'==*p)) { - if ('_'==*p) { - p++; - continue; - } - number = number * 10. + (*p - '0'); - p++; - num_digits_total++; - } - integral_part = number; - - /* Done? */ - if (0==*p) { - if (endptr) - *endptr = p; - if (sign==-1) - return -number; - return number; - } - - /* Do we have a fractional part? */ - if ('.'==*p) { - p++; - - /* keep on skipping prefixed zeros (i.e. allow writing 1e-20 */ - /* as 0.00000000000000000001 without losing precision) */ - if (0==integral_part) - while ('0'==*p || '_'==*p) { - if ('0'==*p) - num_prefixed_zeros++; - p++; - } - - /* if the next character is nonnumeric, we have reached the end */ - if (0==*p || 0==strchr ("_0123456789eE+-", *p)) { - if (endptr) - *endptr = p; - if (sign==-1) - return -number; - return number; - } - - while (isdigit(*p) || '_'==*p) { - /* Don't let pathologically long fractions destroy precision */ - if ('_'==*p || num_digits_total > 17) { - p++; - continue; - } - - number = number * 10. + (*p - '0'); - if (*p!='0') - fraction_is_nonzero = 1; - p++; - num_digits_total++; - num_digits_after_comma++; - } - - /* Avoid having long zero-tails (4321.000...000) destroy precision */ - if (fraction_is_nonzero) - exponent = -(num_digits_after_comma + num_prefixed_zeros); - else - number = integral_part; - } /* end of fractional part */ - - - /* non-digit */ - if (0==num_digits_total) { - errno = EINVAL; - if (endptr) - *endptr = p; - return HUGE_VAL; - } - - if (sign==-1) - number = -number; - - /* Do we have an exponent part? */ - while (*p == 'e' || *p == 'E') { - p++; - - /* Just a stray "e", as in 100elephants? */ - if (0==*p || 0==strchr ("0123456789+-_", *p)) { - p--; - break; - } - - while ('_'==*p) - p++; - /* Does it have a sign? */ - sign = 0; - if ('-'==*p) - sign = -1; - if ('+'==*p) - sign = +1; - if (0==sign) { - if (!isdigit(*p) && *p!='_') { - if (endptr) - *endptr = p; - return HUGE_VAL; - } - } - else - p++; - - - /* Go on and read the exponent */ - n = 0; - while (isdigit(*p) || '_'==*p) { - if ('_'==*p) { - p++; - continue; - } - n = n * 10 + (*p - '0'); - p++; - } - - if (-1==sign) - n = -n; - exponent += n; - break; - } - - if (endptr) - *endptr = p; - - if ((exponent < DBL_MIN_EXP) || (exponent > DBL_MAX_EXP)) { - errno = ERANGE; - return HUGE_VAL; - } - - /* on some platforms pow() is very slow - so don't call it if exponent is close to 0 */ - if (0==exponent) - return number; - if (abs (exponent) < 20) { - double ex = 1; - int absexp = exponent < 0? -exponent: exponent; - while (absexp--) - ex *= 10; - number = exponent < 0? number / ex: number * ex; - } - else - number *= pow (10, exponent); - - return number; -} - -double proj_atof(const char *str) { - return proj_strtod(str, (void *) 0); -} - -#ifdef TEST - -/* compile/run: gcc -DTEST -o proj_strtod_test proj_strtod.c && proj_strtod_test */ - -#include - -char *un_underscore (char *s) { - static char u[1024]; - int i, m, n; - for (i = m = 0, n = strlen (s); i < n; i++) { - if (s[i]=='_') { - m++; - continue; - } - u[i - m] = s[i]; - } - u[n-m] = 0; - return u; -} - -int thetest (char *s, int line) { - char *endp, *endq, *u; - double p, q; - int errnop, errnoq, prev_errno; - - prev_errno = errno; - - u = un_underscore (s); - - errno = 0; - p = proj_strtod (s, &endp); - errnop = errno; - errno = 0; - q = strtod (u, &endq); - errnoq = errno; - - errno = prev_errno; - - if (q==p && 0==strcmp (endp, endq) && errnop==errnoq) - return 0; - - errno = line; - printf ("Line: %3.3d - [%s] [%s]\n", line, s, u); - printf ("proj_strtod: %2d %.17g [%s]\n", errnop, p, endp); - printf ("libc_strtod: %2d %.17g [%s]\n", errnoq, q, endq); - return 1; -} - -#define test(s) thetest(s, __LINE__) - -int main (int argc, char **argv) { - double res; - char *endptr; - - errno = 0; - - test (""); - test (" "); - test (" abcde"); - test (" edcba"); - test ("abcde"); - test ("edcba"); - test ("+"); - test ("-"); - test ("+ "); - test ("- "); - test (" + "); - test (" - "); - test ("e 1"); - test ("e1"); - test ("0 66"); - test ("1."); - test ("0."); - test ("1.0"); - test ("0.0"); - test ("1 "); - test ("0 "); - test ("-0 "); - test ("0_ "); - test ("0_"); - test ("1e"); - test ("_1.0"); - test ("_0.0"); - test ("1_.0"); - test ("0_.0"); - test ("1__.0"); - test ("0__.0"); - test ("1.__0"); - test ("0.__0"); - test ("1.0___"); - test ("0.0___"); - test ("1e2"); - test ("__123_456_789_._10_11_12"); - test ("1______"); - test ("1___e__2__"); - test ("-1"); - test ("-1.0"); - test ("-0"); - test ("-1e__-_2__rest"); - test ("0.00002"); - test ("0.00001"); - test ("-0.00002"); - test ("-0.00001"); - test ("-0.00001e-2"); - test ("-0.00001e2"); - test ("1e9999"); - - /* We expect this one to differ */ - test ("0.000000000000000000000000000000000000000000000000000000000000000000000000002"); - - if (errno) - printf ("First discrepancy in line %d\n", errno); - - if (argc < 2) - return 0; - res = proj_strtod (argv[1], &endptr); - printf ("res = %20.15g. Rest = [%s], errno = %d\n", res, endptr, (int) errno); - return 0; -} -#endif diff --git a/src/proj_strtod.cpp b/src/proj_strtod.cpp new file mode 100644 index 00000000..05d448ec --- /dev/null +++ b/src/proj_strtod.cpp @@ -0,0 +1,440 @@ +/*********************************************************************** + + proj_strtod: Convert string to double, accepting underscore separators + + Thomas Knudsen, 2017-01-17/09-19 + +************************************************************************ + +Conventionally, PROJ.4 does not honor locale settings, consistently +behaving as if LC_ALL=C. + +For this to work, we have, for many years, been using other solutions +than the C standard library strtod/atof functions for converting strings +to doubles. + +In the early versions of proj, iirc, a gnu version of strtod was used, +mostly to work around cases where the same system library was used for +C and Fortran linking, hence making strtod accept "D" and "d" as +exponentiation indicators, following Fortran Double Precision constant +syntax. This broke the proj angular syntax, accepting a "d" to mean +"degree": 12d34'56", meaning 12 degrees 34 minutes and 56 seconds. + +With an explicit MIT licence, PROJ.4 could not include GPL code any +longer, and apparently at some time, the GPL code was replaced by the +current C port of a GDAL function (in pj_strtod.c), which reads the +LC_NUMERIC setting and, behind the back of the user, momentarily changes +the conventional '.' delimiter to whatever the locale requires, then +calls the system supplied strtod. + +While this requires a minimum amount of coding, it only solves one +problem, and not in a very generic way. + +Another problem, I would like to see solved, is the handling of underscores +as generic delimiters. This is getting popular in a number of programming +languages (Ada, C++, C#, D, Java, Julia, Perl 5, Python, Rust, etc. +cf. e.g. https://www.python.org/dev/peps/pep-0515/), and in our case of +handling numbers being in the order of magnitude of the Earth's dimensions, +and a resolution of submillimetre, i.e. having 10 or more significant digits, +splitting the "wall of digits" into smaller chunks is of immense value. + +Hence this reimplementation of strtod, which hardcodes '.' as indicator of +numeric fractions, and accepts '_' anywhere in a numerical string sequence: +So a typical northing value can be written + + 6_098_907.8250 m +rather than + 6098907.8250 m + +which, in my humble opinion, is well worth the effort. + +While writing this code, I took ample inspiration from Michael Ringgaard's +strtod version over at http://www.jbox.dk/sanos/source/lib/strtod.c.html, +and Yasuhiro Matsumoto's public domain version over at +https://gist.github.com/mattn/1890186. The code below is, however, not +copied from any of the two mentioned - it is a reimplementation, and +probably suffers from its own set of bugs. So for now, it is intended +not as a replacement of pj_strtod, but only as an experimental piece of +code for use in an experimental new transformation program, cct. + +************************************************************************ + +Thomas Knudsen, thokn@sdfe.dk, 2017-01-17/2017-09-18 + +************************************************************************ + +* Copyright (c) 2017 Thomas Knudsen & SDFE +* +* 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 "proj_strtod.h" + +#include /* for abs */ +#include /* for strchr */ +#include +#include +#include /* for HUGE_VAL */ +#include /* for pow() */ + + +double proj_strtod(const char *str, char **endptr) { + double number = 0, integral_part = 0; + int exponent = 0; + int fraction_is_nonzero = 0; + int sign = 0; + char *p = (char *) str; + int n = 0; + int num_digits_total = 0; + int num_digits_after_comma = 0; + int num_prefixed_zeros = 0; + + if (0==str) { + errno = EFAULT; + if (endptr) + *endptr = p; + return HUGE_VAL; + } + + /* First skip leading whitespace */ + while (isspace(*p)) + p++; + + /* Empty string? */ + if (0==*p) { + if (endptr) + *endptr = (char *) str; + return 0; + } + + /* non-numeric? */ + if (0==strchr("0123456789+-._", *p)) { + if (endptr) + *endptr = (char *) str; + return 0; + } + + /* Then handle optional prefixed sign and skip prefix zeros */ + switch (*p) { + case '-': + sign = -1; + p++; + break; + case '+': + sign = 1; + p++; + break; + default: + if (isdigit(*p) || '_'==*p || '.'==*p) + break; + if (endptr) + *endptr = (char *) str; + return 0; + } + + /* stray sign, as in "+/-"? */ + if (0!=sign && (0==strchr ("0123456789._", *p) || 0==*p)) { + if (endptr) + *endptr = (char *) str; + return 0; + } + + /* skip prefixed zeros before '.' */ + while ('0'==*p || '_'==*p) + p++; + + /* zero? */ + if ((0==*p) || 0==strchr ("0123456789eE.", *p) || isspace(*p)) { + if (endptr) + *endptr = p; + return sign==-1? -0: 0; + } + + /* Now expect a (potentially zero-length) string of digits */ + while (isdigit(*p) || ('_'==*p)) { + if ('_'==*p) { + p++; + continue; + } + number = number * 10. + (*p - '0'); + p++; + num_digits_total++; + } + integral_part = number; + + /* Done? */ + if (0==*p) { + if (endptr) + *endptr = p; + if (sign==-1) + return -number; + return number; + } + + /* Do we have a fractional part? */ + if ('.'==*p) { + p++; + + /* keep on skipping prefixed zeros (i.e. allow writing 1e-20 */ + /* as 0.00000000000000000001 without losing precision) */ + if (0==integral_part) + while ('0'==*p || '_'==*p) { + if ('0'==*p) + num_prefixed_zeros++; + p++; + } + + /* if the next character is nonnumeric, we have reached the end */ + if (0==*p || 0==strchr ("_0123456789eE+-", *p)) { + if (endptr) + *endptr = p; + if (sign==-1) + return -number; + return number; + } + + while (isdigit(*p) || '_'==*p) { + /* Don't let pathologically long fractions destroy precision */ + if ('_'==*p || num_digits_total > 17) { + p++; + continue; + } + + number = number * 10. + (*p - '0'); + if (*p!='0') + fraction_is_nonzero = 1; + p++; + num_digits_total++; + num_digits_after_comma++; + } + + /* Avoid having long zero-tails (4321.000...000) destroy precision */ + if (fraction_is_nonzero) + exponent = -(num_digits_after_comma + num_prefixed_zeros); + else + number = integral_part; + } /* end of fractional part */ + + + /* non-digit */ + if (0==num_digits_total) { + errno = EINVAL; + if (endptr) + *endptr = p; + return HUGE_VAL; + } + + if (sign==-1) + number = -number; + + /* Do we have an exponent part? */ + while (*p == 'e' || *p == 'E') { + p++; + + /* Just a stray "e", as in 100elephants? */ + if (0==*p || 0==strchr ("0123456789+-_", *p)) { + p--; + break; + } + + while ('_'==*p) + p++; + /* Does it have a sign? */ + sign = 0; + if ('-'==*p) + sign = -1; + if ('+'==*p) + sign = +1; + if (0==sign) { + if (!isdigit(*p) && *p!='_') { + if (endptr) + *endptr = p; + return HUGE_VAL; + } + } + else + p++; + + + /* Go on and read the exponent */ + n = 0; + while (isdigit(*p) || '_'==*p) { + if ('_'==*p) { + p++; + continue; + } + n = n * 10 + (*p - '0'); + p++; + } + + if (-1==sign) + n = -n; + exponent += n; + break; + } + + if (endptr) + *endptr = p; + + if ((exponent < DBL_MIN_EXP) || (exponent > DBL_MAX_EXP)) { + errno = ERANGE; + return HUGE_VAL; + } + + /* on some platforms pow() is very slow - so don't call it if exponent is close to 0 */ + if (0==exponent) + return number; + if (abs (exponent) < 20) { + double ex = 1; + int absexp = exponent < 0? -exponent: exponent; + while (absexp--) + ex *= 10; + number = exponent < 0? number / ex: number * ex; + } + else + number *= pow (10, exponent); + + return number; +} + +double proj_atof(const char *str) { + return proj_strtod(str, nullptr); +} + +#ifdef TEST + +/* compile/run: gcc -DTEST -o proj_strtod_test proj_strtod.c && proj_strtod_test */ + +#include + +char *un_underscore (char *s) { + static char u[1024]; + int i, m, n; + for (i = m = 0, n = strlen (s); i < n; i++) { + if (s[i]=='_') { + m++; + continue; + } + u[i - m] = s[i]; + } + u[n-m] = 0; + return u; +} + +int thetest (char *s, int line) { + char *endp, *endq, *u; + double p, q; + int errnop, errnoq, prev_errno; + + prev_errno = errno; + + u = un_underscore (s); + + errno = 0; + p = proj_strtod (s, &endp); + errnop = errno; + errno = 0; + q = strtod (u, &endq); + errnoq = errno; + + errno = prev_errno; + + if (q==p && 0==strcmp (endp, endq) && errnop==errnoq) + return 0; + + errno = line; + printf ("Line: %3.3d - [%s] [%s]\n", line, s, u); + printf ("proj_strtod: %2d %.17g [%s]\n", errnop, p, endp); + printf ("libc_strtod: %2d %.17g [%s]\n", errnoq, q, endq); + return 1; +} + +#define test(s) thetest(s, __LINE__) + +int main (int argc, char **argv) { + double res; + char *endptr; + + errno = 0; + + test (""); + test (" "); + test (" abcde"); + test (" edcba"); + test ("abcde"); + test ("edcba"); + test ("+"); + test ("-"); + test ("+ "); + test ("- "); + test (" + "); + test (" - "); + test ("e 1"); + test ("e1"); + test ("0 66"); + test ("1."); + test ("0."); + test ("1.0"); + test ("0.0"); + test ("1 "); + test ("0 "); + test ("-0 "); + test ("0_ "); + test ("0_"); + test ("1e"); + test ("_1.0"); + test ("_0.0"); + test ("1_.0"); + test ("0_.0"); + test ("1__.0"); + test ("0__.0"); + test ("1.__0"); + test ("0.__0"); + test ("1.0___"); + test ("0.0___"); + test ("1e2"); + test ("__123_456_789_._10_11_12"); + test ("1______"); + test ("1___e__2__"); + test ("-1"); + test ("-1.0"); + test ("-0"); + test ("-1e__-_2__rest"); + test ("0.00002"); + test ("0.00001"); + test ("-0.00002"); + test ("-0.00001"); + test ("-0.00001e-2"); + test ("-0.00001e2"); + test ("1e9999"); + + /* We expect this one to differ */ + test ("0.000000000000000000000000000000000000000000000000000000000000000000000000002"); + + if (errno) + printf ("First discrepancy in line %d\n", errno); + + if (argc < 2) + return 0; + res = proj_strtod (argv[1], &endptr); + printf ("res = %20.15g. Rest = [%s], errno = %d\n", res, endptr, (int) errno); + return 0; +} +#endif diff --git a/src/projects.h b/src/projects.h index 11467d56..ac1a2152 100644 --- a/src/projects.h +++ b/src/projects.h @@ -197,7 +197,6 @@ struct PJconsts; union PJ_COORD; struct geod_geodesic; -struct pj_opaque; struct ARG_list; struct PJ_REGION_S; typedef struct PJ_REGION_S PJ_Region; @@ -260,7 +259,7 @@ PJ_OPERATOR: *****************************************************************************/ typedef PJ *(* PJ_CONSTRUCTOR) (PJ *); -typedef void *(* PJ_DESTRUCTOR) (PJ *, int); +typedef PJ *(* PJ_DESTRUCTOR) (PJ *, int); typedef PJ_COORD (* PJ_OPERATOR) (PJ_COORD, PJ *); /****************************************************************************/ @@ -290,7 +289,7 @@ struct PJconsts { char *def_ellps; struct geod_geodesic *geod; /* For geodesic computations */ - struct pj_opaque *opaque; /* Projection specific parameters, Defined in PJ_*.c */ + void *opaque; /* Projection specific parameters, Defined in PJ_*.c */ int inverted; /* Tell high level API functions to swap inv/fwd */ @@ -828,7 +827,7 @@ extern char const PROJ_DLL pj_release[]; struct PJ_DATUMS PROJ_DLL *pj_get_datums_ref( void ); -void *pj_default_destructor (PJ *P, int errlev); +PJ *pj_default_destructor (PJ *P, int errlev); double PROJ_DLL pj_atof( const char* nptr ); double pj_strtod( const char *nptr, char **endptr ); diff --git a/src/rtodms.c b/src/rtodms.c deleted file mode 100644 index 674cebdf..00000000 --- a/src/rtodms.c +++ /dev/null @@ -1,86 +0,0 @@ -/* Convert radian argument to DMS ascii format */ - -#include -#include -#include -#include - -#include "projects.h" - -/* -** RES is fractional second figures -** RES60 = 60 * RES -** CONV = 180 * 3600 * RES / PI (radians to RES seconds) -*/ - static double -RES = 1000., -RES60 = 60000., -CONV = 206264806.24709635516; - static char -format[50] = "%dd%d'%.3f\"%c"; - static int -dolong = 0; - void -set_rtodms(int fract, int con_w) { - int i; - - if (fract >= 0 && fract < 9 ) { - RES = 1.; - /* following not very elegant, but used infrequently */ - for (i = 0; i < fract; ++i) - RES *= 10.; - RES60 = RES * 60.; - CONV = 180. * 3600. * RES / M_PI; - if (! con_w) - (void)sprintf(format,"%%dd%%d'%%.%df\"%%c", fract); - else - (void)sprintf(format,"%%dd%%02d'%%0%d.%df\"%%c", - fract+2+(fract?1:0), fract); - dolong = con_w; - } -} - char * -rtodms(char *s, double r, int pos, int neg) { - int deg, min, sign; - char *ss = s; - double sec; - - if (r < 0) { - r = -r; - if (!pos) { *ss++ = '-'; sign = 0; } - else sign = neg; - } else - sign = pos; - r = floor(r * CONV + .5); - sec = fmod(r / RES, 60.); - r = floor(r / RES60); - min = (int)fmod(r, 60.); - r = floor(r / 60.); - deg = (int)r; - - if (dolong) - (void)sprintf(ss,format,deg,min,sec,sign); - else if (sec != 0.0) { - char *p, *q; - /* double prime + pos/neg suffix (if included) + NUL */ - size_t suffix_len = sign ? 3 : 2; - - (void)sprintf(ss,format,deg,min,sec,sign); - /* Replace potential decimal comma by decimal point for non C locale */ - for( p = ss; *p != '\0'; ++p ) { - if( *p == ',' ) { - *p = '.'; - break; - } - } - for (q = p = ss + strlen(ss) - suffix_len; *p == '0'; --p) ; - if (*p != '.') - ++p; - if (++q != p) - (void)memmove(p, q, suffix_len); - } else if (min) - (void)sprintf(ss,"%dd%d'%c",deg,min,sign); - else - (void)sprintf(ss,"%dd%c",deg, sign); - return s; -} diff --git a/src/rtodms.cpp b/src/rtodms.cpp new file mode 100644 index 00000000..674cebdf --- /dev/null +++ b/src/rtodms.cpp @@ -0,0 +1,86 @@ +/* Convert radian argument to DMS ascii format */ + +#include +#include +#include +#include + +#include "projects.h" + +/* +** RES is fractional second figures +** RES60 = 60 * RES +** CONV = 180 * 3600 * RES / PI (radians to RES seconds) +*/ + static double +RES = 1000., +RES60 = 60000., +CONV = 206264806.24709635516; + static char +format[50] = "%dd%d'%.3f\"%c"; + static int +dolong = 0; + void +set_rtodms(int fract, int con_w) { + int i; + + if (fract >= 0 && fract < 9 ) { + RES = 1.; + /* following not very elegant, but used infrequently */ + for (i = 0; i < fract; ++i) + RES *= 10.; + RES60 = RES * 60.; + CONV = 180. * 3600. * RES / M_PI; + if (! con_w) + (void)sprintf(format,"%%dd%%d'%%.%df\"%%c", fract); + else + (void)sprintf(format,"%%dd%%02d'%%0%d.%df\"%%c", + fract+2+(fract?1:0), fract); + dolong = con_w; + } +} + char * +rtodms(char *s, double r, int pos, int neg) { + int deg, min, sign; + char *ss = s; + double sec; + + if (r < 0) { + r = -r; + if (!pos) { *ss++ = '-'; sign = 0; } + else sign = neg; + } else + sign = pos; + r = floor(r * CONV + .5); + sec = fmod(r / RES, 60.); + r = floor(r / RES60); + min = (int)fmod(r, 60.); + r = floor(r / 60.); + deg = (int)r; + + if (dolong) + (void)sprintf(ss,format,deg,min,sec,sign); + else if (sec != 0.0) { + char *p, *q; + /* double prime + pos/neg suffix (if included) + NUL */ + size_t suffix_len = sign ? 3 : 2; + + (void)sprintf(ss,format,deg,min,sec,sign); + /* Replace potential decimal comma by decimal point for non C locale */ + for( p = ss; *p != '\0'; ++p ) { + if( *p == ',' ) { + *p = '.'; + break; + } + } + for (q = p = ss + strlen(ss) - suffix_len; *p == '0'; --p) ; + if (*p != '.') + ++p; + if (++q != p) + (void)memmove(p, q, suffix_len); + } else if (min) + (void)sprintf(ss,"%dd%d'%c",deg,min,sign); + else + (void)sprintf(ss,"%dd%c",deg, sign); + return s; +} diff --git a/src/test228.c b/src/test228.c deleted file mode 100644 index 83d29f8f..00000000 --- a/src/test228.c +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H -#endif - -#include "proj_api.h" -#include /* for printf declaration */ - - -#ifdef _WIN32 - -int main(int argc, char* argv[]) -{ - printf("Test not yet ported on Win32\n"); - return 0; -} - -#else - -#include -#include -#include -#include - -static volatile int run = 0; -static volatile int started = 0; - -static void* thread_main(void* unused) -{ - projCtx p_proj_ctxt; - projPJ p_WGS84_proj; - projPJ p_OSGB36_proj; - (void)unused; - - __sync_add_and_fetch(&started, 1); - while(run == 0); - - p_proj_ctxt=pj_ctx_alloc(); - p_WGS84_proj=pj_init_plus_ctx(p_proj_ctxt,"+proj=longlat " - "+ellps=WGS84 +datum=WGS84 +no_defs"); - p_OSGB36_proj=pj_init_plus_ctx(p_proj_ctxt, - "+proj=longlat +ellps=airy +datum=OSGB36 +nadgrids=OSTN02_NTv2.gsb " - "+no_defs"); - - while(run) - { - double x, y; - int proj_ret; - - x = -5.2*DEG_TO_RAD; - y = 50*DEG_TO_RAD; - proj_ret = pj_transform(p_WGS84_proj, - p_OSGB36_proj, 1, 1, &x, &y, NULL ); - x *= RAD_TO_DEG; - y *= RAD_TO_DEG; - /*printf("%.18f %.18f\n", x, y); */ - assert(proj_ret == 0); - assert(fabs(x - -5.198965360936369962) < 1e-15); - assert(fabs(y - 49.999396034285531698) < 1e-15); - } - - pj_free (p_OSGB36_proj); - pj_free (p_WGS84_proj); - return NULL; -} - -int main() -{ - int i; - - pthread_t tid1, tid2; - pthread_attr_t attr1, attr2; - - pthread_attr_init(&attr1); - pthread_attr_init(&attr2); - - pthread_create(&tid1, &attr1, thread_main, NULL); - pthread_create(&tid2, &attr2, thread_main, NULL); - while(started != 2); - run = 1; - for(i=0;i<2;i++) - sleep(1); - run = 0; - return 0; -} - -#endif /* _WIN32 */ diff --git a/src/test228.cpp b/src/test228.cpp new file mode 100644 index 00000000..83d29f8f --- /dev/null +++ b/src/test228.cpp @@ -0,0 +1,86 @@ +#ifndef ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#define ACCEPT_USE_OF_DEPRECATED_PROJ_API_H +#endif + +#include "proj_api.h" +#include /* for printf declaration */ + + +#ifdef _WIN32 + +int main(int argc, char* argv[]) +{ + printf("Test not yet ported on Win32\n"); + return 0; +} + +#else + +#include +#include +#include +#include + +static volatile int run = 0; +static volatile int started = 0; + +static void* thread_main(void* unused) +{ + projCtx p_proj_ctxt; + projPJ p_WGS84_proj; + projPJ p_OSGB36_proj; + (void)unused; + + __sync_add_and_fetch(&started, 1); + while(run == 0); + + p_proj_ctxt=pj_ctx_alloc(); + p_WGS84_proj=pj_init_plus_ctx(p_proj_ctxt,"+proj=longlat " + "+ellps=WGS84 +datum=WGS84 +no_defs"); + p_OSGB36_proj=pj_init_plus_ctx(p_proj_ctxt, + "+proj=longlat +ellps=airy +datum=OSGB36 +nadgrids=OSTN02_NTv2.gsb " + "+no_defs"); + + while(run) + { + double x, y; + int proj_ret; + + x = -5.2*DEG_TO_RAD; + y = 50*DEG_TO_RAD; + proj_ret = pj_transform(p_WGS84_proj, + p_OSGB36_proj, 1, 1, &x, &y, NULL ); + x *= RAD_TO_DEG; + y *= RAD_TO_DEG; + /*printf("%.18f %.18f\n", x, y); */ + assert(proj_ret == 0); + assert(fabs(x - -5.198965360936369962) < 1e-15); + assert(fabs(y - 49.999396034285531698) < 1e-15); + } + + pj_free (p_OSGB36_proj); + pj_free (p_WGS84_proj); + return NULL; +} + +int main() +{ + int i; + + pthread_t tid1, tid2; + pthread_attr_t attr1, attr2; + + pthread_attr_init(&attr1); + pthread_attr_init(&attr2); + + pthread_create(&tid1, &attr1, thread_main, NULL); + pthread_create(&tid2, &attr2, thread_main, NULL); + while(started != 2); + run = 1; + for(i=0;i<2;i++) + sleep(1); + run = 0; + return 0; +} + +#endif /* _WIN32 */ diff --git a/src/vector1.c b/src/vector1.c deleted file mode 100644 index 22e1f5d0..00000000 --- a/src/vector1.c +++ /dev/null @@ -1,29 +0,0 @@ -/* make storage for one and two dimensional matricies */ -#include -#include "projects.h" - void * /* one dimension array */ -vector1(int nvals, int size) { return((void *)pj_malloc(size * nvals)); } - void /* free 2D array */ -freev2(void **v, int nrows) { - if (v) { - for (v += nrows; nrows > 0; --nrows) - pj_dalloc(*--v); - pj_dalloc(v); - } -} - void ** /* two dimension array */ -vector2(int nrows, int ncols, int size) { - void **s; - - if ((s = (void **)pj_malloc(sizeof(void *) * nrows)) != NULL) { - int rsize, i; - - rsize = size * ncols; - for (i = 0; i < nrows; ++i) - if (!(s[i] = pj_malloc(rsize))) { - freev2(s, i); - return (void **)0; - } - } - return s; -} diff --git a/src/vector1.cpp b/src/vector1.cpp new file mode 100644 index 00000000..22e1f5d0 --- /dev/null +++ b/src/vector1.cpp @@ -0,0 +1,29 @@ +/* make storage for one and two dimensional matricies */ +#include +#include "projects.h" + void * /* one dimension array */ +vector1(int nvals, int size) { return((void *)pj_malloc(size * nvals)); } + void /* free 2D array */ +freev2(void **v, int nrows) { + if (v) { + for (v += nrows; nrows > 0; --nrows) + pj_dalloc(*--v); + pj_dalloc(v); + } +} + void ** /* two dimension array */ +vector2(int nrows, int ncols, int size) { + void **s; + + if ((s = (void **)pj_malloc(sizeof(void *) * nrows)) != NULL) { + int rsize, i; + + rsize = size * ncols; + for (i = 0; i < nrows; ++i) + if (!(s[i] = pj_malloc(rsize))) { + freev2(s, i); + return (void **)0; + } + } + return s; +} -- cgit v1.2.3